見出し画像

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)
                    }
           }




この記事が気に入ったらサポートをしてみませんか?