Как использовать один и тот же набор модификаторов для разных форм

В рамках моего обучающего проекта 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))))
        
    }
}

person Dawy    schedule 26.06.2020    source источник


Ответы (2)


Вы можете абстрагироваться от некоторых повторений, используя вспомогательные функции и расширения.

  1. В упрощенном примере ниже я использую @ViewBuilder, чтобы очистить код, который мы возвращаем. Нет необходимости использовать AnyView, и это значительно упрощает чтение кода.

    Было бы здорово, если бы мы могли вернуть some Shape, но в настоящее время это невозможно и приводит к ошибкам. Вот почему значение штриха должно повторяться для каждого Shape в функции getShape, иначе мы могли бы сделать расширение на Shape вместо View.

  2. Я создаю расширение для View, которое позволяет нам объединять модификаторы в одну функцию, делая ее более читаемой и простой в использовании. Хотя, честно говоря, эта часть не является обязательной, и вы можете использовать два ваших модификатора frame и rotationEffect.

  3. @ViewBuilder getShape(shape:index:) возвращает выбранную вами форму с выбранным цветом, затем он используется функцией createShape(shape:index:), где вы добавляете настраиваемый модификатор, который мы создали как расширение для View.

  4. Наконец мы создаем нашу форму

Это должно дать вам отправную точку.

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()
    }
}

Очень жаль, что мы не можем вернуть some Shape из @ViewBuilder или если бы существовал @ShapeBuilder, поскольку это означало бы, что нам не нужно было бы добавлять обводку к каждой форме по отдельности, поскольку View не может иметь обводку.

person Andrew    schedule 26.06.2020

Использование вспомогательного ластика типа 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))))
}
person Asperi    schedule 27.06.2020