Не удается вывести общий тип «S» в «Текст» SwiftUI в проекте CS193p, в Xcode

Фон

Я прохожу онлайн-курс Stanfords CS193p iOS Development. Я с Xcode 11.3 и Swift 5.1.2.

Проблема

К концу лекции 5. ViewBuilder + Shape + ViewModifier у меня возникла ошибка, заключающаяся в том, что компилятор Swift сообщил, что «общий параметр« S »не может быть выведен». Снимок: IDE жалуется на это

Фрагменты кода

Код в EmojiMemoryGameView.swift

import SwiftUI

struct EmojiMemoryGameView: View {
    @ObservedObject var viewModel: EmojiMemoryGame
    var body: some View {
        Grid(viewModel.cards) { card in
            CardView(card: card).onTapGesture(perform: {
                self.viewModel.choose(card: card)
            })
            .padding(5)
        }
            .padding()
            .foregroundColor(Color.orange)
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        let game = EmojiMemoryGame()
        game.choose(card: game.cards[0])
        return EmojiMemoryGameView(viewModel: game)
    }
}


struct CardView: View {
    var card: MemoryGame<String>.Card
    
    var body: some View {
        GeometryReader { geometry in
            self.body(for: geometry.size)
        }
    }
    
    @ViewBuilder
    private func body(for size: CGSize) -> some View {
        if card.isFaceUp || !card.isMatched {
            ZStack {
                Pie(startAngle: Angle.degrees(-90),
                    endAngle: Angle.degrees(-10),
                    clockwise: true)
                    .padding(5)
                    .opacity(0.3)
                Text(card.content)
                    .font(Font.system(size: fontSize(for: size)))
                }.cardify(isFaceUp: card.isFaceUp)
            //.modifier(Cardify(isFaceUp: card.isFaceUp))
        }
    }
    
    
    private func fontSize(for size: CGSize) -> CGFloat {
        return min(size.width, size.height) * fontScaleFactor
    }
    // MARK: - Drawing Constants
    
    private let fontScaleFactor: CGFloat = 0.75
}

}

Код в MemoryGame.swift

import Foundation

struct MemoryGame<CardContent> where CardContent: Equatable { // costraints and gains
    private(set) var cards: Array<Card>
    private var indexOfTheOneAndOnlyFaceUpCard: Int? {
        get {
            cards.indices.filter { cards[$0].isFaceUp }.only
        }
        set {
            for index in cards.indices {
                // newValue is a var only appears in set method
                cards[index].isFaceUp = index == newValue
            }
        }
    }
    
    init(numberOfPairsOfCards: Int, cardContentFactory: (Int) -> CardContent) {
        cards = Array<Card>()
        let maxIndex = 2 * numberOfPairsOfCards - 1
        var pairIndices = [Int]()
        for i in 0...maxIndex {
            let randomNumber = Double.random(in: 0...1)
            if randomNumber >= 0.5 {
                pairIndices.insert(i/2, at: 0)
            } else {
                pairIndices.append(i/2)
            }
        }
        for i in 0...maxIndex {
            let pairIndex = pairIndices[i]
            let content = cardContentFactory(pairIndex)
            cards.append(Card(id: i, content: content))
        }
    }
                    
    mutating func choose(card: Card) {
        print("card chosen: \(card)")
        if let chosenIndex = cards.firstIndex(matching: card), !cards[chosenIndex].isFaceUp, !cards[chosenIndex].isMatched {
            // comma here is like a sequential "AND"
            if let potentialMatchIndex = indexOfTheOneAndOnlyFaceUpCard {
                if cards[chosenIndex].content ==  cards[potentialMatchIndex].content {
                    cards[chosenIndex].isMatched = true
                    cards[potentialMatchIndex].isMatched = true
                }
            } else {
                indexOfTheOneAndOnlyFaceUpCard = chosenIndex
            }
            self.cards[chosenIndex].isFaceUp = true
        }
    }
    
    struct Card: Identifiable {
        /* Member variable 'id' is essential for the Identifiable protocol */
        var id: Int
        
        var isFaceUp: Bool = false
        var isMatched: Bool = false
        var content: CardContent
    }
}

Однако, когда я щелкаю переменную, удерживая клавишу Alt, она ясно показывает, что тип String. Снимок: объявление типа

Код моего текущего проекта также доступен на Github.

Так почему же произошла ошибка и как я могу ее исправить?

Будем признательны за вашу помощь!


person ht.lee    schedule 29.07.2020    source источник
comment
Пожалуйста, скопируйте и вставьте код в свой вопрос. :). (Я вижу, вы добавили ссылку на github, но это будет быстрее, если вы также разместите код здесь.)   -  person Shamas S    schedule 29.07.2020
comment
@ShamasS-ReinstateMonica Спасибо за совет! Код прилагается. Я тут новенький :)   -  person ht.lee    schedule 29.07.2020
comment
и вам очень рады здесь. :)   -  person Shamas S    schedule 29.07.2020


Ответы (1)


В вашем struct CardView: View у вас есть

if card.isFaceUp || !card.isMatched {
            ZStack {
                Pie(startAngle: Angle.degrees(-90),
                    endAngle: Angle.degrees(-10),
                    clockwise: true)
                    .padding(5)
                    .opacity(0.3)
                Text(card.content)
                    .font(Font.system(size: fontSize(for: size)))
                }.cardify(isFaceUp: card.isFaceUp)
            //.modifier(Cardify(isFaceUp: card.isFaceUp))
        }

добавьте return перед ZStack, и это должно решить вашу проблему.

if card.isFaceUp || !card.isMatched {
                return ZStack {
                    Pie(startAngle: Angle.degrees(-90),
                        endAngle: Angle.degrees(-10),
                        clockwise: true)
                        .padding(5)
                        .opacity(0.3)
                    Text(card.content)
                        .font(Font.system(size: fontSize(for: size)))
                    }.cardify(isFaceUp: card.isFaceUp)
                //.modifier(Cardify(isFaceUp: card.isFaceUp))
            }
person Shamas S    schedule 29.07.2020
comment
Это работает, но это потрясающе! Большое спасибо. Так что же происходит после того, как я добавил возврат и в чем разница? - person ht.lee; 29.07.2020
comment
без возврата компилятор может автоматически определить тип возвращаемого значения и сопоставить его с сигнатурой функции. Но если у нас есть несколько операторов внутри, что в вашем случае является оператором if, вы должны явно указать, какой из них возвращается. - person Shamas S; 29.07.2020