【问题标题】:Extracting SwiftUI view into new view将 SwiftUI 视图提取到新视图中
【发布时间】:2021-05-25 05:53:56
【问题描述】:

有没有办法将配置的视图提取到它自己的视图结构中,以便可以应用某些自定义?例如,我在 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() 之后会导致填充区域而不是剪切区域。

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

【问题讨论】:

  • 你可以像你提到的那样创建一个新视图或扩展 Button 或创建一个新的按钮样式,但我想这不是这里的问题,而是如何处理修饰符?
  • 是的,为了与其他 SwiftUI 配置保持一致,能够以类似的方式应用修饰符会很好。

标签: swift swiftui


【解决方案1】:

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 修饰符将不起作用——但我们应该能够将.bold() 应用于字体声明本身(这需要 Xcode 12.5 - 在早期版本中,您需要切换到 Font.custom... 才能以这种方式链接字体修饰符)。

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

Button(action: {}) { Text("Some Button Text") }
  .buttonStyle(FullWidthButtonStyle(backgroundColor: Color("user-profile-action-call1"))

Button(action: {}) { Text("Some other button text") }
  .buttonStyle(FullWidthButtonStyle(backgroundColor: .orange))

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

【讨论】:

    【解决方案2】:

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

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

    你可以像这样创建一个新的视图修饰符:

    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)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-10-19
      • 1970-01-01
      • 2020-08-05
      • 2020-07-22
      • 1970-01-01
      • 2020-04-15
      • 1970-01-01
      • 2020-03-16
      相关资源
      最近更新 更多