ios - SwiftUI:通过 View 组合实现代码重用

标签 ios swift swiftui

使用 SwiftUI 进行开发时,我发现很难一起重用代码组合 View 。我将向您展示一个简单的示例:假设我们的应用程序中有一个具有特定 UI 的文本字段。我们将此文本字段称为 MyTextField。用户界面可能是:

enter image description here

这是代码:

struct MyTextField: View {
    @Binding var text: String
    var label: String

    var body: some View {
        VStack {
            HStack {
                Text(label)
                Spacer()
            }
            TextField("", text: $text) //here we have a simple TextField
            Divider()
        }
        .padding()
    }
}

现在,假设我们想要另一个具有相同 UI 的文本字段,但要在安全上下文中使用。该文本字段称为MySecureTextField。在这种情况下,我应该使用 SecureField 而不是 TextField,但显然我不想以这种方式创建一个全新的 View :

struct MySecureTextField: View {
    @Binding var text: String
    var label: String

    var body: some View {
        VStack {
            HStack {
                Text(label)
                Spacer()
            }
            SecureField("", text: $text) //this time we have a SecureField here
            Divider()
        }
        .padding()
    }
}

如何设计这样的情况?我尝试了几种方法,但似乎没有一种是正确的:

1 - 第一次尝试要有一种将实际文本字段作为参数的容器 View :

struct TextFieldContainer<ActualTextField>: View where ActualTextField: View {
    private let actualTextField: () -> ActualTextField
    var label: String

    init(label: String, @ViewBuilder actualTextField: @escaping () -> ActualTextField) {
        self.label = label
        self.actualTextField = actualTextField
    }

    var body: some View {
        VStack {
            HStack {
                Text(label)
                Spacer()
            }
            actualTextField()
            Divider()
        }
        .padding()
    }
}

我可以这样使用TextFieldContainer:

struct ContentView: View {
    @State private var text = ""

    var body: some View {
        TextFieldContainer(label: "Label") {
            SecureField("", text: self.$text)
        }
    }
}

我不喜欢这个解决方案:我不想指定实际的文本字段,它应该隐含在 View 本身中(MyTextFieldMySecureTextField)。这样我什至可以在容器内注入(inject)任何类型的 View ,而不仅仅是文本字段。

2 - 第二次尝试拥有一个私有(private)容器和两个在内部使用该容器的公共(public) View :

private struct TextFieldContainer<ActualTextField>: View where ActualTextField: View {
    //...
    //the same implementation as above
    //...
}

struct MyTextField: View {
    @Binding var text: String //duplicated code (see MySecureTextField)
    let label: String //duplicated code (see MySecureTextField)

    var body: some View {
        TextFieldContainer(label: label) {
            TextField("", text: self.$text)
        }
    }
}

struct MySecureTextField: View {
    @Binding var text: String //duplicated code (see MyTextField)
    let label: String //duplicated code (see MyTextField)

    var body: some View {
        TextFieldContainer(label: label) {
            SecureField("", text: self.$text)
        }
    }
}

并以这种方式使用它们:

struct ContentView: View {
    @State private var text = ""
    @State private var text2 = ""

    var body: some View {
        VStack {
            MyTextField(text: $text, label: "Label")
            MySecureTextField(text: $text2, label: "Secure textfield")
        }
    }
}

我并不是真的不喜欢这个解决方案,但属性上有一些代码重复。如果有很多属性,就会有很多代码重复。另外,如果我更改了 TextFieldContainer 上的某些属性,我应该相应地更改所有 View ,可能需要更改很多结构(MyTextFieldMySecureTextField >、MyEmailTextFieldMyBlaBlaTextField 等)。

3 - 我的最后一次尝试 使用与上面第二次尝试相同的方法,但使用 AnyView 的方式如下:

struct MySecureTextField: View {
    private let content: AnyView

    init(text: Binding<String>, label: String) {
        content = AnyView(TextFieldContainer(label: label) {
            SecureField("", text: text)
        })
    }

    var body: some View {
        content
    }
}

struct MyTextField: View {
    private let content: AnyView

    init(text: Binding<String>, label: String) {
        content = AnyView(TextFieldContainer(label: label) {
            TextField("", text: text)
        })
    }

    var body: some View {
        content
    }
}

这与第二次尝试没有什么不同,我的直觉是我缺少执行此常见任务的正确方法(SwiftUI-y 方法)。您能否指出正确的“设计模式”或者改进我描述的解决方案之一?抱歉问了这么长的问题。

最佳答案

您可以使用简单的 if!

struct MyTextField: View {
    @Binding var text: String
    var label: String
    var secure: Bool = false

    var body: some View {
        VStack {
            HStack {
                Text(label)
                Spacer()
            }

            if secure {
                SecureField("", text: $text)
            } else {
                TextField("", text: $text)
            }

            Divider()
        }
        .padding()
    }
}

用法:

MyTextField(text: $text, label: "Label") // unsecure
MyTextField(text: $text, label: "Label", secure: true) // secure

关于ios - SwiftUI:通过 View 组合实现代码重用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58105911/

相关文章:

swift - AVFoundation 类未在 OSX xcode 项目中解析

ios - 尝试以编程方式添加 UITableView() 时未在 UIViewController 中显示

ios - 将 UITableView 与不同的 View 模型类型重用

swift - 将 Swift 2.0 转换为 Swift 3.0

swift - 新的 iOS 开发者应该使用 Storyboard 和 Xibs/Nibs 吗?

SwiftUI 导出或共享文件

ios - nil 不是用于搜索实体名称 'GroupMessageInfo' 的合法 NSPersistentStoreCoordinator

ios - 长按模式下的 Collection View 时,如何移动 Collection View 单元格并在 iOS 中重新排列它们?

ios - 适用于 iOS 的 Apache Cordova (VS2015) : Adding custom entries to *info. plist 工具

ios - 如何获取 iOS 系统设置中列出的所有可用声音文件