获取形状以在 SwiftUI 中动态查看

Rya*_*ner 5 swift swiftui swift5.2

使用 Swift 5.2 我想创建一个函数来动态更改形状

我有一个像

import SwiftUI

struct CardView: View {
    let suit : Suite
    let rank : Rank
    var body: some View {
        getShape(suite: .heart)
        .fill(Color.red)  // .fill(suit.color)
        .frame(width: 100, height: 100)
     }
}
Run Code Online (Sandbox Code Playgroud)

我想创建一个协议返回类型为 Shape 的函数,我在下面的示例中将自定义形状替换为通用

func getShape(suite:Suite) -> Shape {
    switch suite {
    case .heart:
        return Circle() // Heart()
    case .diamond:
        return Rectangle() // Diamond()
    case .spade:
        return Circle() // Heart()
    case .club:
        return Circle() // Club()

    }
}
Run Code Online (Sandbox Code Playgroud)

我不能对某些不透明类型使用不透明类型,因为我返回不同的类型并且出现编译错误

Function declares an opaque return type, but the return statements in its body do not have matching underlying types 
Run Code Online (Sandbox Code Playgroud)

我也不能保持协议类型不变,因为我收到错误

Protocol 'Shape' can only be used as a generic constraint because it has Self or associated type requirements
Run Code Online (Sandbox Code Playgroud)

有什么办法可以优雅地实现这一目标吗?

Rya*_*ner 9

通过将@Asperi 的回答与

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
}
Run Code Online (Sandbox Code Playgroud)

我可以把它改成

func getShape(suite:Suite) -> some Shape {
    switch suite {
    case .club:
        return AnyShape(Club())
    case .diamond:
        return AnyShape(Diamond())
    case .heart:
        return AnyShape(Heart())

    case .spade:
        return AnyShape(Spade())
    }
}


struct CardView: View {
    let suit : Suite
    let rank : Rank
    var body: some View {

    getShape(suite: suit)
      .fill(Color.red)
      .frame(width: 100, height: 100)
 }
Run Code Online (Sandbox Code Playgroud)


Asp*_*eri 5

这是可能的解决方案。使用 Xcode 11.4 测试。

struct CardView: View {
    let suit : Suite
    let rank : Rank
    var body: some View {
        // pass all dependencies to generate view
        getShape(suite: .heart, fill: suit.color) 
            .frame(width: 100, height: 100)
     }
}

// Generate complete view and return opaque type
func getShape(suite: Suite, fill color: Color) -> some View {
    switch suite {
        case .heart:
            return AnyView(Heart().fill(color))
        case .diamond:
            return AnyView(Diamond().fill(color))
        case .spade:
            return AnyView(Spade().fill(color))
        case .club:
            return AnyView(Club().fill(color))
   }
}
Run Code Online (Sandbox Code Playgroud)


D. *_*ter 5

只是想把这个留在这里:

https://github.com/ohitsdaniel/ShapeBuilder

我最近开源了一个 ShapeBuilder,它允许通过利用结果构建器将计算属性和函数标记为@ShapeBuilder@InsettableShapeBuilder避免类型擦除。

这将允许您编写以下代码:

import ShapeBuilder

@ShapeBuilder func getShape(suite:Suite) -> some Shape {
  switch suite {
    case .heart:
     Heart()
    case .diamond:
     Diamond()
    case .spade:
     Heart()
    case .club:
     Club()
  }
}
Run Code Online (Sandbox Code Playgroud)

我还建议不要擦除 AnyView,如前面的答案所述。相反,标记您可以使用 @ViewBuilder 标记您的 getShape 函数。这会将函数体变成视图构建器,就像 SwiftUI View body 属性一样,并避免类型擦除,从而使 SwiftUI 能够更轻松地维护结构视图标识。