使用Apple最新的Swift UI技術(shù)編寫更好的app(4)

實現(xiàn)grid布局窒升,實現(xiàn)翻牌規(guī)則

之前我們卡片都在一行功炮,在某個方向上浪費了很多空間。我們希望卡片分布在多行也就是網(wǎng)格狀布局肋演。在目前的siwft中抑诸,并沒有這樣的布局,需要我們自己實現(xiàn)爹殊。

我們創(chuàng)建一個 Grid 結(jié)構(gòu)體蜕乡,來實現(xiàn)網(wǎng)格布局。

struct Grid<Item,ItemView>: View where Item:Identifiable, ItemView:View {
    var items:[Item]
    var viewForItem: (Item) -> ItemView
    
    init(items:[Item], viewForItem:@escaping (Item)->ItemView) {
        self.items = items
        self.viewForItem = viewForItem
    }
    
    var body: some View {
        GeometryReader { gemory in
            body(for: GridLayout(itemCount: items.count, in: gemory.size))
        }
    }
    
    func body(for layout:GridLayout) -> some View {
        ForEach(items) { item in
            let index = items.firstIndex(matching:item)!
            viewForItem(item)
                .frame(width:layout.itemSize.width,height:layout.itemSize.height)
                .position(layout.location(ofItemAt: index))
        }
    }
    
}

我們需要知道 items 以及根據(jù)item生成對應(yīng)的視圖梗夸。并計算他們的大小擺放他們的位置层玲。這是我們這個結(jié)構(gòu)體要實現(xiàn)的。

viewForItem是一個函數(shù)類型的反症,在初始化方法中需要添加關(guān)鍵字 @escaping 來表明這是一個逃逸閉包辛块。這個關(guān)鍵字讓程序知道 這個初始化很快會執(zhí)行完,但函數(shù)會延遲調(diào)用铅碍,不會隨著init函數(shù)一起調(diào)用润绵,而是在將來某些時候觸發(fā)調(diào)用。這里要使用@escaping告訴計算機胞谈。

我們可以通過GeometryReader來獲取分配的空間大小尘盼,然后使用GridLayout來計算每個卡片的大小和位置。

使用ForEach 函數(shù)時烦绳,需要參數(shù)是有唯一標示的卿捎,而我們的Item我們并不關(guān)心是什么內(nèi)容,但是函數(shù)要求Item需要是遵循了Identifiable協(xié)議的径密,因此我們要限制一下我們的結(jié)構(gòu)體午阵,要求Grid結(jié)構(gòu)體中用到的Item是遵循了Identifiable協(xié)議的。在這里我們又一次使用了泛型的概念享扔。同樣的趟庄,viewForItem函數(shù)要求接受一個item括细,返回一個view,ItemView要求遵循了View協(xié)議戚啥。

struct Grid<Item,ItemView>: View where Item:Identifiable, ItemView:View {}

在我們的視圖中奋单,將原來的HStack換成我們自己寫的網(wǎng)格布局,并設(shè)置卡片之間的間距猫十。

struct EmojiMemoryGameView: View {
   @ObservedObject var viewModel:EmojiMemoryGame
    var body: some View {
        Grid(items: viewModel.cards) { card in
            GridView(card: card).onTapGesture {
                viewModel.shoose(card: card)
            }.padding(cardPadding)
        }
        .padding()
        .foregroundColor(.orange)
    }
    let cardPadding:CGFloat = 5
}
image.png

接下來我們要完善一下游戲規(guī)則览濒。

啟動時所有卡片都是反面

點開第一個卡片時,翻開卡片

點開第二個卡片時拖云,和第一個卡片進行對比

點開第三張卡片時贷笛,合上其他卡片

我們處理一下我們選擇卡片的邏輯代碼:

我們需要定義一個變量來跟蹤正面朝上的那個卡片的位置索引var indexOfTheOneAndOnlyFaceupCard:Int?剛開始沒有正面朝上,所以這是個可選值宙项。

我們要判斷選擇的卡片是否是正面朝上以及是否已經(jīng)匹配乏苦。

   mutating func choose(card:Card) {
        print("card choosen:\(card)")
    if let choosenIndex = cards.firstIndex(matching: card), !cards[choosenIndex].isFaceUp, !cards[choosenIndex].isMactched {
        if let potentialMactchIndex = indexOfTheOneAndOnlyFaceupCard {
            if cards[choosenIndex].content == cards[potentialMactchIndex].content {
                // matched
                cards[choosenIndex].isMactched = true
                cards[potentialMactchIndex].isMactched = true
            }
               indexOfTheOneAndOnlyFaceupCard = nil
        }else {
              for index in cards.indices {
              cards[index].isFaceUp = false
        }
            indexOfTheOneAndOnlyFaceupCard = choosenIndex
        }
           cards[choosenIndex].isFaceUp = true
        }
    }

然后我們運行后發(fā)現(xiàn),被匹配之后的卡片雖然正面朝下尤筐,但是已經(jīng)不能點擊了汇荐。這樣的界面行為很不好。我們希望已經(jīng)匹配成功的卡片不再顯示盆繁。我們在視圖代碼中掀淘,修改顯示卡片背面的邏輯,只有沒有匹配的卡片才進行繪制油昂。

ZStack {
                if card.isFaceUp {
                RoundedRectangle(cornerRadius: conerRadius).fill(Color.white)
                RoundedRectangle(cornerRadius: conerRadius).stroke(lineWidth: edgeLineWidth)
                    Text(card.content)
                }else {
                    if !card.isMactched {RoundedRectangle(cornerRadius: conerRadius).fill(Color.orange)}
                }
            }

上面處理了界面革娄。下面我們來優(yōu)化下選擇卡片之后的代碼。

我們把indexOfTheOneAndOnlyFaceupCard賦值之后邏輯代碼寫在一起冕碟,將indexOfTheOneAndOnlyFaceupCard作為計算屬性拦惋,在set方法和get方法中處理邏輯

    var indexOfTheOneAndOnlyFaceupCard:Int? {
        get {
             cards.indices.filter{cards[$0].isFaceUp}.only
        }
        set {
            for index in cards.indices {
                cards[index].isFaceUp = index == newValue
            }
        }
    }

這樣的話 choose中的代碼就可以更簡單了

   mutating func choose(card:Card) {
    if let choosenIndex = cards.firstIndex(matching: card), !cards[choosenIndex].isFaceUp, !cards[choosenIndex].isMactched {
        if let potentialMactchIndex = indexOfTheOneAndOnlyFaceupCard {
            if cards[choosenIndex].content == cards[potentialMactchIndex].content {
                // matched
                cards[choosenIndex].isMactched = true
                cards[potentialMactchIndex].isMactched = true
            }
            cards[choosenIndex].isFaceUp = true
        }else {
            indexOfTheOneAndOnlyFaceupCard = choosenIndex
        }
        }
    }

代碼中還做了其他的優(yōu)化,今天的效果圖如下:

dayfour.gif

個人主頁有GitHub地址主頁安寺,查看源碼請點擊:https://github.com/MyColourfulLife/MySwiftUI

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末厕妖,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子我衬,更是在濱河造成了極大的恐慌,老刑警劉巖饰恕,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挠羔,死亡現(xiàn)場離奇詭異,居然都是意外死亡埋嵌,警方通過查閱死者的電腦和手機破加,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來雹嗦,“玉大人范舀,你說我怎么就攤上這事合是。” “怎么了锭环?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵聪全,是天一觀的道長。 經(jīng)常有香客問我辅辩,道長难礼,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任玫锋,我火速辦了婚禮蛾茉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘撩鹿。我一直安慰自己谦炬,他們只是感情好,可當我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布节沦。 她就那樣靜靜地躺著键思,像睡著了一般。 火紅的嫁衣襯著肌膚如雪散劫。 梳的紋絲不亂的頭發(fā)上稚机,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天,我揣著相機與錄音获搏,去河邊找鬼赖条。 笑死,一個胖子當著我的面吹牛常熙,可吹牛的內(nèi)容都是我干的纬乍。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼裸卫,長吁一口氣:“原來是場噩夢啊……” “哼仿贬!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起墓贿,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤茧泪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后聋袋,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體队伟,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年幽勒,在試婚紗的時候發(fā)現(xiàn)自己被綠了嗜侮。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖锈颗,靈堂內(nèi)的尸體忽然破棺而出顷霹,到底是詐尸還是另有隱情,我是刑警寧澤击吱,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布淋淀,位于F島的核電站,受9級特大地震影響姨拥,放射性物質(zhì)發(fā)生泄漏绅喉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一叫乌、第九天 我趴在偏房一處隱蔽的房頂上張望柴罐。 院中可真熱鬧,春花似錦憨奸、人聲如沸革屠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽似芝。三九已至,卻和暖如春板甘,著一層夾襖步出監(jiān)牢的瞬間党瓮,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工盐类, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留寞奸,地道東北人。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓在跳,卻偏偏與公主長得像枪萄,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子猫妙,可洞房花燭夜當晚...
    茶點故事閱讀 44,724評論 2 354