Ошибка: значение типа some View не имеет элемента stroke

Я слежу за онлайн-курсом Stanfords CS193p «Разработка приложений для iOS». Я использую Xcode 11.5. (Я не обновлял, потому что эту версию использует инструктор курса (Пол Хигарти).)

Я пытаюсь выполнить задание 3 (Установить игру). В настоящее время (чтобы избежать дублирования кода) я пытаюсь заменить этот фрагмент:

            VStack {
                ForEach(0..<numberOfShapes) { index in
                    if self.card.shape == .diamond {
                        ZStack {
                            Diamond().fill()
                                .opacity(self.opacity)
                            Diamond().stroke(lineWidth: self.shapeEdgeLineWidth)
                        }
                    }
                    if self.card.shape == .squiggle {
                        ZStack {
                            Rectangle().fill()
                            Rectangle().stroke(lineWidth: self.shapeEdgeLineWidth)
                        }
                    }
                    if self.card.shape == .oval {
                        ZStack {
                            Ellipse().fill()
                            Ellipse().stroke(lineWidth: self.shapeEdgeLineWidth)
                        }
                    }
                }
            }

С этим фрагментом:

            VStack {
                ForEach(0..<numberOfShapes) { index in
                    ZStack {
                        shape(self.card.shape).opacity(self.opacity)
                        shape(self.card.shape).stroke(lineWidth: 2.5) // ERROR here: Value of type 'some View' has no member 'stroke'
                    }
                }
            }

И эта функция @ViewBuilder:

@ViewBuilder
func shape(_ shape: SetGameModel.Card.Shape) -> some View {
    if shape == .diamond {
        Diamond()
    } else if shape == .squiggle {
        Rectangle()
    } else {
        Ellipse()
    }
}

А вот и полный код просмотра:

import SwiftUI

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

struct CardView: View {
    var card: SetGameModel.Card

    var numberOfShapes: Int {
        switch card.numberOfShapes {
        case .one:
            return 1
        case .two:
            return 2
        case .three:
            return 3
        }
    }
        
    var opacity: Double {
        switch card.shading {
        case .open:
            return 0.0
        case .solid: // filled
            return 1.0
        case .striped: // you can use a semi-transparent color to represent the “striped” shading.
            return 0.33
        }
    }
    
    var color: Color {
        switch card.color {
        case .green:
            return Color.green
        case .purple:
            return Color.purple
        case .red:
            return Color.red
        }
    }
    
    var body: some View {
        ZStack {
            RoundedRectangle(cornerRadius: cornerRadius).fill(Color.white)
            RoundedRectangle(cornerRadius: cornerRadius)
                .stroke(lineWidth: card.isChosen ? chosenCardEdgeLineWidth : normalCardEdgeLineWidth)
                .foregroundColor(card.isChosen ? Color.red : Color.orange)
            VStack {
                ForEach(0..<numberOfShapes) { index in
                    ZStack {
                        shape(self.card.shape).opacity(self.opacity)
                        shape(self.card.shape).stroke(lineWidth: 2.5) // ERROR here: Value of type 'some View' has no member 'stroke'
                    }
                }
            }
            .foregroundColor(self.color)
            .padding()
        }
        .aspectRatio(cardAspectRatio, contentMode: .fit)
    }
    
    // MARK: - Drawing Constants
    let cornerRadius: CGFloat = 10.0
    let chosenCardEdgeLineWidth: CGFloat = 6
    let normalCardEdgeLineWidth: CGFloat = 3
    let shapeEdgeLineWidth: CGFloat = 2.5
    let cardAspectRatio: CGSize = CGSize(width: 2, height: 3) // 2/3 aspectRatio
}

@ViewBuilder
func shape(_ shape: SetGameModel.Card.Shape) -> some View {
    if shape == .diamond {
        Diamond()
    } else if shape == .squiggle {
        Rectangle()
    } else {
        Ellipse()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        SetGameView(viewModel: SetGameViewModel())
    }
}

И у меня такая ошибка:

Value of type 'some View' has no member 'stroke'

Я не могу понять этого, что не так? Как я могу это исправить?

Пожалуйста, попробуйте помочь мне исправить это как-нибудь, что я пойму как новичок ????

Кстати, Diamond () - это моя кастомная форма (например, Rectangle ()). Если вам также нужны ViewModel, Model или другие файлы, которые помогут мне исправить это, дайте мне знать :-)


person user14119170    schedule 03.12.2020    source источник
comment
Это дает мне эту ошибку: Тип возврата глобальной функции 'shape' требует, чтобы '_ConditionalContent ‹_ConditionalContent‹ Diamond, Rectangle ›, Ellipse›' соответствовал 'Shape'   -  person user14119170    schedule 03.12.2020


Ответы (1)


stroke определяется Shape, а не View, и вы возвращаете Shapes, а не только Views. Вам нужно изменить тип возвращаемого значения shape на some Shape.

К сожалению, @ViewBuilder требуется, чтобы возвращаемый тип был some View, а не some Shape, поэтому вам нужно удалить атрибут @ViewBuilder и убедиться, что ваша функция возвращает одинаковый Shape из каждой ветви. Для этого вы можете реализовать Shape со стиранием типа, называемый AnyShape, аналогичный AnyView для View, и возвращать версию каждого Shape со стиранием типа.

struct AnyShape: Shape {
    init<S: Shape>(_ wrapped: S) {
        _path = { rect in
            let path = wrapped.path(in: rect)
            return path
        }
    }

    func path(in rect: CGRect) -> Path {
        return _path(rect)
    }

    private let _path: (CGRect) -> Path
}

func shape(_ shape: SetGameModel.Card.Shape) -> some Shape {
    if shape == .diamond {
        return AnyShape(Diamond())
    } else if shape == .squiggle {
        return AnyShape(Rectangle())
    } else {
        return AnyShape(Ellipse())
    }
}
person Dávid Pásztor    schedule 03.12.2020
comment
Это дает мне эту ошибку: Тип возврата глобальной функции 'shape' требует, чтобы '_ConditionalContent ‹_ConditionalContent‹ Diamond, Rectangle ›, Ellipse›' соответствовал 'Shape' - person user14119170; 03.12.2020
comment
@ user14119170 проверьте мой обновленный ответ, теперь код компилируется нормально - person Dávid Pásztor; 04.12.2020
comment
Спасибо! :-). - person user14119170; 04.12.2020