如何对不同的形状使用相同的修饰符集

Posted

技术标签:

【中文标题】如何对不同的形状使用相同的修饰符集【英文标题】:How to use same set of modifiers for various shapes 【发布时间】:2020-06-26 20:10:03 【问题描述】:

作为我学习 SwiftUI 项目的一部分,我做了一些形状旋转,下面有代码。我想知道如何避免每个形状使用相同的三行修饰符。

func getShape(shape: Int, i: Int) -> AnyView 
    
    switch shape 
    case 0:
        return AnyView(Rectangle()
                        .stroke(colors[Int(shapeColor)])
                        .frame(width: CGFloat(shapeWidth), height: CGFloat(shapeHeight))
                        .rotationEffect(Angle(degrees: Double(i) * Double(angleStep))))
    case 1:
        return AnyView(Capsule()
                        .stroke(colors[Int(shapeColor)])
                        .frame(width: CGFloat(shapeWidth), height: CGFloat(shapeHeight))
                        .rotationEffect(Angle(degrees: Double(i) * Double(angleStep))))
    case 2:
        return AnyView(Ellipse()
                        .stroke(colors[Int(shapeColor)])
                        .frame(width: CGFloat(shapeWidth), height: CGFloat(shapeHeight))
                        .rotationEffect(Angle(degrees: Double(i) * Double(angleStep))))
    default:
        return AnyView(Rectangle()
                        .stroke(colors[Int(shapeColor)])
                        .frame(width: CGFloat(shapeWidth), height: CGFloat(shapeHeight))
                        .rotationEffect(Angle(degrees: Double(i) * Double(angleStep))))
        
    

【问题讨论】:

【参考方案1】:

使用助手AnyShape 类型橡皮擦

struct AnyShape: Shape 
    private let builder: (CGRect) -> Path

    init<S: Shape>(_ shape: S) 
        builder =  rect in
            let path = shape.path(in: rect)
            return path
        
    

    func path(in rect: CGRect) -> Path 
        return builder(rect)
    

你的函数可以写成

func getShape(shape: Int, i: Int) -> some View 
    let selectedShape: AnyShape = 
        switch shape 
            case 0:
                return AnyShape(Rectangle())
            case 1:
                return AnyShape(Capsule())
            case 2:
                return AnyShape(Ellipse())
            default:
                return AnyShape(Rectangle())
        
    ()
    return selectedShape
        .stroke(colors[Int(shapeColor)])
        .frame(width: CGFloat(shapeWidth), height: CGFloat(shapeHeight))
        .rotationEffect(Angle(degrees: Double(i) * Double(angleStep))))

【讨论】:

【参考方案2】:

您可以使用辅助函数和扩展来抽象出一些重复。

    在下面的简化示例中,我使用@ViewBuilder 来清理我们返回的代码。无需使用AnyView,它使代码更易于阅读。

    如果我们能返回 some Shape 那就太好了,但这是 目前不可能并导致错误。这就是为什么中风 必须为 getShape 中的每个 Shape 重复值 函数,否则我们可以在Shape 上进行扩展 而不是View

    我在View 上创建了一个扩展,它允许我们将修饰符组合到一个函数中,使其更具可读性和更易于使用。老实说,这部分是可选的,您可以使用两个修饰符 framerotationEffect

    @ViewBuilder getShape(shape:index:) 返回您选择的形状及其选择的颜色,然后由函数 createShape(shape:index:) 使用,您可以在其中添加我们在 View 上作为扩展创建的自定义修饰符。

    最后我们创建了我们的形状

这应该给你一个起点。

struct ShapeView: View 

    @ViewBuilder // 1
    func getShape(shape: Int, i: Int) -> some View 
        switch shape 
        case 0:
            Rectangle().stroke(Color.red)
        case 1:
            Capsule().stroke(Color.red)
        case 2:
            Ellipse().stroke(Color.red)
        default:
            Rectangle().stroke(Color.red)
        
    

    func createShape(shape: Int, index: Int) -> some View  // 3
        getShape(shape: shape, i: index)
            .myModifier(width: 200, height: 100, index: index, angleStep: 30)
    

    var body: some View 
        createShape(shape: 2, index: 1) // 4
    

  
// 2
extension View 
    func myModifier(width: CGFloat, height: CGFloat, index: Int, angleStep: Double) -> some View 
        self
            .frame(width: width, height: height)
            .rotationEffect(Angle(degrees: Double(index) * Double(angleStep)))
    


struct ShapeView_Previews: PreviewProvider 
    static var previews: some View 
        ShapeView()
    


很遗憾,我们不能从@ViewBuilder 返回some Shape,或者如果存在@ShapeBuilder,因为这意味着我们不必将笔划单独添加到每个形状,作为@ 987654338@不能中风。

【讨论】:

【参考方案3】:

嵌套函数可以帮助清理代码:

func getShape(shape: Int, i: Int) -> some View 
    
    func adjustedView<S: Shape>(shape: S) -> some View 
        shape
            .stroke(colors[Int(shapeColor)])
            .frame(width: CGFloat(shapeWidth), height: CGFloat(shapeHeight))
            .rotationEffect(Angle(degrees: Double(i) * Double(angleStep))))
    
    
    return Group 
        switch shape 
        case 0:
            adjustedView(shape: Rectangle())
        case 1:
            adjustedView(shape: Capsule())
        case 2:
            adjustedView(shape: Ellipse())
        default:
            adjustedView(shape: Rectangle())
        
    

另一种选择是使用便利功能扩展Shape。即

extension Shape 
    func adjust(shapeWidth: Double, shapeHeight: Double, angle: Angle) -> some View 
        self.stroke()
            //.stroke(colors[Int(shapeColor)]) // for brevity
            .frame(width: CGFloat(shapeWidth), height: CGFloat(shapeHeight))
            .rotationEffect(angle)
    

它稍微简化了代码。也不需要擦除类型。

func getShape(shape: Int, i: Int) -> some View 
    Group 
        switch shape 
        case 0:
             Rectangle().adjust(shapeWidth: shapeWidth, shapeHeight: shapeHeight, angle: Angle(degrees: Double(i) * Double(angleStep))))
        case 1:
             Capsule().adjust(shapeWidth: shapeWidth, shapeHeight: shapeHeight, angle: Angle(degrees: Double(i) * Double(angleStep))))
        case 2:
            Ellipse().adjust(shapeWidth: shapeWidth, shapeHeight: shapeHeight, angle: Angle(degrees: Double(i) * Double(angleStep))))
        default:
            Rectangle().adjust(shapeWidth: shapeWidth, shapeHeight: shapeHeight, angle: Angle(degrees: Double(i) * Double(angleStep))))
        
    

【讨论】:

以上是关于如何对不同的形状使用相同的修饰符集的主要内容,如果未能解决你的问题,请参考以下文章

Koltin——最详细的可见性修饰符详解

Java 访问权限修饰符 与 非访问权限修饰符

Swift 函数中的动态修饰符

Java中的修饰符

不同脸型的修饰方法 根据脸型选择合适的妆容

抽象与多态