7

使用 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)
     }
}

我想创建一个协议返回类型为 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()

    }
}

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

Function declares an opaque return type, but the return statements in its body do not have matching underlying types 

我也不能将它与协议类型保持原样,因为我收到错误

Protocol 'Shape' can only be used as a generic constraint because it has Self or associated type requirements

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

4

3 回答 3

12

通过将@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
}

我可以将其更改为

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)
 }
于 2020-04-29T20:31:04.403 回答
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))
   }
}
于 2020-04-29T15:56:57.800 回答
3

只是想把这个留在这里:

https://github.com/ohitsdaniel/ShapeBuilder

我最近开源了一个 ShapeBuilder ,它计算属性和函数为@ShapeBuilderor @InsettableShapeBuilder。这避免了类型擦除。

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

import ShapeBuilder

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

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

于 2021-08-12T10:20:43.750 回答