#swift #swiftui #cs193p
#swift #swiftui #cs193p
Вопрос:
Я следую онлайн-курсу CS193p по разработке приложений для iOS от Stanfords. Я использую 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 или некоторые другие файлы, чтобы помочь мне исправить это, дайте мне знать 🙂
Комментарии:
1. Это дает мне следующую ошибку: возвращаемый тип глобальной функции ‘shape’ требует, чтобы ‘_ConditionalContent<_ConditionalContent <Ромб, прямоугольник>, эллипс>’ соответствовал ‘Shape’
Ответ №1:
stroke
определяется на Shape
, а не View
и вы возвращаете Shape
s, а не только View
s. Вам нужно изменить возвращаемый тип shape
to some Shape
.
К сожалению @ViewBuilder
, тип возвращаемого значения должен быть some View
, not some Shape
, поэтому вам нужно удалить @ViewBuilder
атрибут и убедиться, что ваша функция возвращает то же Shape
самое из каждой ветви. Для достижения этого вы можете реализовать стираемый тип Shape
, вызываемый AnyShape
аналогично AnyView
for 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())
}
}
Комментарии:
1.Это дает мне следующую ошибку: возвращаемый тип глобальной функции ‘shape’ требует, чтобы ‘_ConditionalContent<_ConditionalContent <Ромб, прямоугольник>, эллипс>’ соответствовал ‘Shape’
2. @user14119170 проверьте мой обновленный ответ, теперь код компилируется просто отлично