将 SwiftUI 视图提取到新视图中

时间:2021-05-25 05:53:56

标签: swift swiftui

有没有办法将配置的视图提取到它自己的视图结构中,以便可以应用某些自定义?例如,我在 UI 的几个地方配置了 Button

    Button(action: {}) {
        Text("Some Button Text")
            .font(.custom("SomeFont", size: 17.0))
            .fontWeight(.bold)
    }
    .frame(maxWidth: .infinity)
    .frame(height: 50.0)
    .foregroundColor(.black)
    .background(Color("user-profile-action-call1"))
    .clipShape(RoundedRectangle(cornerSize: CGSize(width: 10.0, height: 10.0)))
    .padding([.leading, .trailing], 20.0)
    .padding(.top, 33.0)

我希望能够重复使用它,指定不同的按钮文本和不同的背景颜色。显然,一种方法是创建一个新的 View,它将 StringColor 作为参数/属性,并像这样使用它:

    MyButton(title: "Some Button Text", color: Color("user-profile-action-call1"))

但这与 SwiftUI 不一致:

    MyButton(title: "Some Button Text")
        .background(Color("user-profile-action-call1"))

不幸的是,将背景放置在 .clipShape() 之后会导致填充区域被填充,而不是剪切区域。

有时我也想改变其他方面。但我不确定如何制作真正的自定义视图。

2 个答案:

答案 0 :(得分:2)

Grey's answer 在将修饰符组合成自定义修饰符方面是一个很好的方法,但我认为更惯用的 SwiftUI 方式是创建按钮样式

自定义按钮样式是符合 ButtonStyle 协议的结构体;为了满足协议要求,您需要提供一个 makeBody 函数,该函数将您的样式应用于按钮并返回一个新视图。与任何其他视图或修饰符一样,您可以声明变量来初始化您的结构以使样式可定制。

在您的情况下,我会提取除填充值以外的所有内容:

struct FullWidthButtonStyle: ButtonStyle {
    var backgroundColor: Color
    
    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .font(.custom("SomeFont", size: 17.0).bold())
            .frame(maxWidth: .infinity)
            .frame(height: 50.0)
            .foregroundColor(.black)
            .background(backgroundColor)
            .clipShape(RoundedRectangle(cornerSize: CGSize(width: 10.0, height: 10.0)))
    }
}

请注意,因为我们将字体应用于 Button 而不是直接应用于 Text,所以 .fontWeight 修饰符将不起作用 - 但我们应该能够应用 {{ 1}} 到字体声明本身(这需要 Xcode 12.5 - 在早期版本中,您需要切换到 .bold() 才能以这种方式链接字体修饰符)。

然后您可以将样式应用于任何按钮,指定您喜欢的任何背景颜色:

Font.custom...

当谈到 ViewModifier 与 ButtonStyle 时,我认为后者为您提供了一点额外的语义优势:您只想将此样式应用于按钮而不是其他任何东西,但视图修饰符有点过于通用传达这一点。

答案 1 :(得分:0)

自定义视图修饰符可以做到这一点,但您必须记住 SwiftUI 如何创建视图。视图由视图修饰符修改,每个修饰符返回一个视图。因此,应用修饰符的顺序很重要。

在应用其他视图修改器之后,不可能覆盖视图修改器(如其背景颜色)(如果您想要与更改原始背景完全相同的结果,则不能)。

您可以像这样创建一个新的视图修改器:

struct ButtonModifier: ViewModifier {
    var color:Color
    
    func body(content: Content) -> some View {
        content
            .frame(maxWidth: .infinity)
            .frame(height: 50.0)
            .foregroundColor(.black)
            .background(color)
            .clipShape(RoundedRectangle(cornerSize: CGSize(width: 10.0, height: 10.0)))
            .padding([.leading, .trailing], 20.0)
            .padding(.top, 33.0)
    }
}

extension Button {
    func roundedWithBackground(_ color:Color) -> some View {
        self.modifier(ButtonModifier(color: color))
    }
}

这将允许您使用以下“更类似于 SwiftUI”的语法在代码中的其他地方使用您的按钮:

Button(action: {}) {
                Text(title)
                    .font(.custom("SomeFont", size: 17.0))
                    .fontWeight(.bold)
        }.roundedWithBackground(.red)

显然,您可以根据需要向此视图修改器添加更多参数,或者如果您将某些按钮参数包装到它们自己的视图中,则可以这样调用:

MyButton(title: "Some Button Text")
    .roundedWithBackground(.red)