在SwiftUI中创建ViewModifier和View扩展之间的区别

Tim*_*ich 3 ios swift swiftui

我试图找出这两种方法之间的实际区别。例如:

struct PrimaryLabel: ViewModifier {
    func body(content: Content) -> some View {
        content
            .padding()
            .background(Color.black)
            .foregroundColor(Color.white)
            .font(.largeTitle)
            .cornerRadius(10)
    }
}

extension View {
    func makePrimaryLabel() -> some View {
        self
            .padding()
            .background(Color.black)
            .foregroundColor(Color.white)
            .font(.largeTitle)
            .cornerRadius(10)
    }
}
Run Code Online (Sandbox Code Playgroud)

然后我们可以按以下方式使用所有这些:

Text(tech.title)
    .modifier(PrimaryLabel())
Text(tech.title)
    .makePrimaryLabel()
ModifiedContent(
    content: Text(tech.title),
    modifier: PrimaryLabel()
)
Run Code Online (Sandbox Code Playgroud)

Moj*_*ini 17

你提到的所有方法都是正确的。不同之处在于您如何使用它以及访问它的位置。哪一个更好?是一个基于意见的问题,您应该查看干净的代码策略和 SOLID 原则等,以找到每种情况的最佳实践。

由于SwiftUI是非常修饰链基础,第二个选项最接近原始修饰符。你也可以像原件一样采用参数:

extension Text {
    enum Kind {
        case primary
        case secondary
    }

    func style(_ kind: Kind) -> some View {

        switch kind {
        case .primary:
            return self
                .padding()
                .background(Color.black)
                .foregroundColor(Color.white)
                .font(.largeTitle)
                .cornerRadius(10)

        case .secondary:
            return self
                .padding()
                .background(Color.blue)
                .foregroundColor(Color.red)
                .font(.largeTitle)
                .cornerRadius(20)
        }
    }
}

struct ContentView: View {
    @State var kind = Text.Kind.primary

    var body: some View {
        VStack {
        Text("Primary")
            .style(kind)
            Button(action: {
                self.kind = .secondary
            }) {
                Text("Change me to secondary")
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我们应该拭目以待,看看此类新技术的最佳实践是什么。我们现在发现的任何东西都只是一个习惯。

  • `ViewModifier` 可以让你拥有 `@State` 变量,但 View 扩展却不能。 (7认同)
  • @MojtabaHosseini 我知道扩展永远不能有存储变量。这就是我的观点!但 ViewModifier 可以拥有它们,我发布了一个示例。 (2认同)
  • 对于这个问题:只创建一个自定义视图怎么样?它们有不同的应用。ViewModifier(带有 State 变量)为您可以在 ViewModifier 中执行的操作开辟了新的可能性。 (2认同)

Bro*_*son 7

我相信最好的方法是将 ViewModifiers 和 View 扩展结合起来。这将允许在 ViewModifier 中组合 @State 并方便 View 扩展。

struct PrimaryLabel: ViewModifier {
    func body(content: Content) -> some View {
        content
            .padding()
            .background(Color.black)
            .foregroundColor(Color.white)
            .font(.largeTitle)
            .cornerRadius(10)
    }
}

extension View {
    func makePrimaryLabel() -> some View {
        ModifiedContent(content: self, modifier: PrimaryLabel())
    }
}
Run Code Online (Sandbox Code Playgroud)

用法

Text(tech.title)
    .makePrimaryLabel()
Run Code Online (Sandbox Code Playgroud)

  • 顺便说一句,这也是苹果在自己的文档中推荐的:https://developer.apple.com/documentation/swiftui/reducing-view-modifier-maintenance (2认同)

kon*_*iki 5

我通常更喜欢扩展,因为它们可以为您提供更具可读性的代码,并且编写起来通常更短。实际上,我目前正在撰写带有一些技巧的文章。我完成了一篇关于View扩展的文章,可在此处获得

但是,存在差异。最后一个。使用ViewModifier,您可以使用@State变量,但不能使用View扩展名。这是一个例子:

struct ContentView: View {
    var body: some View {
        VStack {
            Text("Hello, how are you?").modifier(ColorChangeOnTap())
        }
    }
}

struct ColorChangeOnTap: ViewModifier {
    @State private var tapped: Bool = false

    func body(content: Content) -> some View {
        return content.foregroundColor(tapped ? .red : .blue).onTapGesture {
            self.tapped.toggle()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 好吧,考虑一下我发布的例子。您将如何以可应用于不同视图的方式编写该逻辑(无需为每个视图重写相同的代码)?您无法使用 View 扩展,因为您无法跟踪内部状态。您不能使用自定义视图,因为您无法重用逻辑以将其应用到其他视图。您只能使用 ViewModifier。(续) (6认同)
  • 至于View扩展的目的是什么?可能有两个原因: 1. 视图扩展不是 SwiftUI 特有的东西。任何结构的扩展都是该语言的一部分。那么如何防止人们使用它们呢?2. 视图扩展看起来比修饰符好得多。因此,当有选择时,我会选择扩展;-) 顺便说一句,你发布的问题很好! (4认同)
  • 这只是一个例子。当然本例不需要。它的唯一目的是向您展示“ViewModifier”内部允许使用“@State”变量。最初的问题不是 View 扩展和 ViewModifier 之间有什么区别吗?给你一个;-) (2认同)
  • 好点子。但我只是想找出为什么苹果开发人员创建这样的结构,如果它不提供任何不透明的优势。:) (2认同)

Cas*_*ser 5

使用 ViewModifier 时,结果视图的类型签名可能有优势。例如,如果我们创建以下 TestView 来显示三个变体的类型:

struct TestView: View {
    init() {
        print("body1: \(type(of: body))")
        print("body2: \(type(of: body2))")
        print("body3: \(type(of: body3))")
    }
    
    @ViewBuilder var body: some View {
        Text("Some Label")
            .modifier(PrimaryLabel())
    }
    
    @ViewBuilder var body2: some View {
        Text("Some Label")
            .makePrimaryLabel()
    }
    
    @ViewBuilder var body3: some View {
        ModifiedContent(
            content: Text("Some Label"),
            modifier: PrimaryLabel()
        )
    }
}
Run Code Online (Sandbox Code Playgroud)

我们可以看到它产生以下类型:

body1: ModifiedContent<Text, PrimaryLabel>
body2: ModifiedContent<ModifiedContent<ModifiedContent<ModifiedContent<ModifiedContent<Text, _PaddingLayout>, _BackgroundStyleModifier<Color>>, _EnvironmentKeyWritingModifier<Optional<Color>>>, _EnvironmentKeyWritingModifier<Optional<Font>>>, _ClipEffect<RoundedRectangle>>
body3: ModifiedContent<Text, PrimaryLabel>
Run Code Online (Sandbox Code Playgroud)

即使在执行过程中没有优势,它也可能使调试变得更容易。