SwiftUIでいこう!- スタンフォード大学Lecture 6: Protocols Shapes
この講座ではまず、
AspectVGrid()
を使えるようにしていきます。最終的には以下となります。
struct ContentView: View {
@ObservedObject var game:EmojiGame
var body: some View {
AspectVGrid(items:game.cards,aspectRatio:2/3,content:{card in
if card.isMatched && !card.isFaceUp{
Rectangle().opacity(0)
}else{
CardView(card:card)
.padding(2)
.onTapGesture {
game.choose(card)
}
}
})
.padding(.horizontal)
.foregroundColor(.red)
}
}
AspectVGrid()を定義を新規ファイル(AspectVGrid.swift)を作ります。構造体は以下。
struct AspectVGrid<Item,ItemView>: View where ItemView:View,Item:Identifiable{}
中身をですが、LazyVGridを使ってカラムレイアウトを組んでいきます。
GeometryReader{geometry in
VStack{
let width:CGFloat = widthThatFit(itemCount: items.count, in:geometry.size, itemAspectratio: aspectRatio)
LazyVGrid(columns: [adaptiveGridItem(width: width)],spacing:0){
ForEach(items){item in
content(item).aspectRatio(aspectRatio,contentMode: .fit)
}
}
Spacer(minLength: 0)
}
}
この中の関数、
func widthThatFit(){}
func adaptiveGridItem(){}
を作ります。
まずは、配列の数、カードの大きさ、縦隔比より配置を決めていくコードとなっています。
private func widthThatFit(itemCount:Int,in size:CGSize,itemAspectratio:CGFloat)->CGFloat{
var colomnCount = 1
var rowCount = itemCount
repeat{
let itemWidth = size.width / CGFloat(colomnCount)
let itemHight = itemWidth / itemAspectratio
if CGFloat(rowCount) * itemHight < size.height{
break
}
colomnCount += 1
rowCount = (itemCount + (colomnCount - 1)) / colomnCount
} while colomnCount < itemCount
if colomnCount > itemCount{
colomnCount = itemCount
}
return floor(size.width / CGFloat(colomnCount))
}と
と、
LazyVGridでグリッドデザインを作っていくための関数。
private func adaptiveGridItem(width:CGFloat) -> GridItem{
var gridItem = GridItem(.adaptive(minimum: width))
gridItem.spacing = 0
return gridItem
}
作ります。
全体は、
struct AspectVGrid<Item,ItemView>: View where ItemView:View,Item:Identifiable{
var items:[Item]
var aspectRatio:CGFloat
var content:(Item)->ItemView
init (items:[Item],aspectRatio:CGFloat,@ViewBuilder content:@escaping(Item)->ItemView){
self.items = items
self.aspectRatio = aspectRatio
self.content = content
}
var body: some View {
GeometryReader{geometry in
VStack{
let width:CGFloat = widthThatFit(itemCount: items.count, in:geometry.size, itemAspectratio: aspectRatio)
LazyVGrid(columns: [adaptiveGridItem(width: width)],spacing:0){
ForEach(items){item in
content(item).aspectRatio(aspectRatio,contentMode: .fit)
}
}
Spacer(minLength: 0)
}
}
}
private func adaptiveGridItem(width:CGFloat) -> GridItem{
var gridItem = GridItem(.adaptive(minimum: width))
gridItem.spacing = 0
return gridItem
}
private func widthThatFit(itemCount:Int,in size:CGSize,itemAspectratio:CGFloat)->CGFloat{
var colomnCount = 1
var rowCount = itemCount
repeat{
let itemWidth = size.width / CGFloat(colomnCount)
let itemHight = itemWidth / itemAspectratio
if CGFloat(rowCount) * itemHight < size.height{
break
}
colomnCount += 1
rowCount = (itemCount + (colomnCount - 1)) / colomnCount
} while colomnCount < itemCount
if colomnCount > itemCount{
colomnCount = itemCount
}
return floor(size.width / CGFloat(colomnCount))
}
}
これを表示部分に適応していきます。
struct ContentView: View {
@ObservedObject var game:EmojiGame
var body: some View {
AspectVGrid(items:game.cards,aspectRatio:2/3,content:{card in
if card.isMatched && !card.isFaceUp{
Rectangle().opacity(0)
}else{
CardView(card:card)
.padding(2)
.onTapGesture {
game.choose(card)
}
}
})
.padding(.horizontal)
.foregroundColor(.red)
}
}
タップして条件判定によりカードが同じであれば透明にして見えなくします。
if card.isMatched && !card.isFaceUp{
Rectangle().opacity(0)
}else{
CardView(card:card)
.padding(2)
.onTapGesture {
game.choose(card)
}
}
この記事が気に入ったらサポートをしてみませんか?