SwiftUI基础篇ComposingViews

ComposingView

概述

文章主要分享SwiftUI Modifier的学习过程,将使用案例的方式进行说明。内容浅显易懂,ComposingView部分调试,不过测试代码是齐全的。如果想要运行结果,可以移步Github下载code -> github案例链接

1、创建和组合自定义视图

SwiftUI的核心之一就是组合,这意味着可以创建许多小视图,然后将他们组合以创建更大、更复杂的视图。这可以大规模的重用视图,这意味着工作量减少了。更好的情况下,组合视图运行时几乎不会造成额外的开销,因此可以随意使用而不用在意性能。这里我复刻一个完整的个人信息的例子。

Employee结构体

人物信息结构体

Swift 复制代码
struct Employee {
    var name: String
    var jobTitle: String
    var emailAddress: String
    var profilePicture: String
}

ProfilePicture头像视图

App中的员工个人资料有头像图片,可以创建一个圆形的视图

Swift 复制代码
struct ProfilePicture: View {
    var imageName: String
    
    var body: some View {
        Image(imageName)
            .resizable()
            .frame(width: 100, height: 100)
            .clipShape(Circle())
    }
}

EmailAddresss视图

Swift 复制代码
struct EmailAddress: View {
    var address: String
        
    var body: some View {
        HStack {
            Image(systemName: "envelope")
            Text(address)
        }
    }
}

EmployeeDetail详细信息视图

Swift 复制代码
struct EmployeeDetail: View {
    var employee: Employee
    
    var body: some View {
        VStack(alignment: .leading) {
            Text(employee.name)
                .font(.largeTitle)
                .foregroundStyle(.primary)
            Text(employee.jobTitle)
                .foregroundStyle(.secondary)
            EmailAddress(address: employee.emailAddress)
        }
    }
}

EmployeeView整合视图

创建一个更大的视图,将ProfilePicture与Employee组合,提供整体的员工信息

Swift 复制代码
struct EmployeeView: View {
    var employee: Employee
    
    var body: some View {
        HStack {
            ProfilePicture(imageName: employee.profilePicture)
            EmployeeDetail(employee: employee)
        }
    }
}

通过分离的结构,可以用很多种方式来展示员工的信息:

  • 只显示头像
  • 只显示电子邮件
  • 只显示员工具体信息
  • 显示所有信息

更重要的是,这意味着当涉及到使用这些struct时,主要的内容视图不必担心如何构建这些内容的布局,因为它只包含一个大的视图,所有的这些布局都被融入到较小的视图中。这就意味着我只要在body中创建一个EmployeeView就可以了。

自定义视图的使用

Swift 复制代码
struct FFCustomView: View {
    //构建数据
    let employee = Employee(name: "Meta BBLv", jobTitle: "Keep Loving, Keep Living", emailAddress: "metabblv@163.com", profilePicture: "chrysanthemum-tea-thumb")
    
    var body: some View {
        EmployeeView(employee: employee)
    }
}

调试结果

2、将文本视图组合在一起

SwiftUI文本视图重载了"+"运算符,可以将文本视图组合创建新的文本视图。当需要在视图中使用不同的格式时,可以使每个文本视图都不一样,然后将它们连接在一起形成单个组合文本视图。最方便的是,当使用朗读功能时,VoiceOver会自动将它们识别为一段文本。

Swift 复制代码
struct FFCustomText: View {
    var body: some View {
        Text("SwiftUI")
            .font(.largeTitle)
        + Text("is")
            .font(.headline)
        + Text("awesome")
            .font(.footnote)
        
        //创建不同颜色或字体的文本
        Text("SwiftUI")
            .foregroundStyle(.red)
        + Text("is")
            .foregroundStyle(.orange)
            .fontWeight(.black)
        + Text("awesome")
            .foregroundStyle(.blue)
    }
}

调试结果

3、将视图存储为属性

如果有多个视图嵌套在另一个视图中,你可能会发现为其中一些或全部视图创建属性很重要,可以使布局代码更容易,然后,可以在视图代码中內联引用这些属性,可以保证视图结构更清晰。

Swift 复制代码
struct FFViewStoreProperties: View {
    let title = Text("metaBBLv").bold()
    let subtitle = Text("Author").foregroundStyle(.secondary)
    
    var body: some View {
        VStack {
            title
            subtitle
            //像这样,只需要在stack中写入属性名就可以使用了。
            //但是更方便的是可以将修饰符附加到这些属性上进行自定义操作。
            Divider()
            title
                .foregroundStyle(.red)
            subtitle
            //这不会改变标题的基础设定,只是在基础程度上附加的一种方式。
        }
    }
}

调试结果

4、创建自定义修饰符

如果发现重复的将同一组修饰符附加到视图(比如,背景色、padding、字体等等),那么可以通过创建一个修饰符来封装这些重复的修饰符。如果你想创建自己的结构,那么要遵守ViewModifier协议。并且要实现一个body(content:)函数。

Swift 复制代码
//创建一个新的PrimaryLabel修饰符,添加padding、background、foregroundcolor和font等
struct PrimaryLabel: ViewModifier {
    func body(content: Content) -> some View {
        content
            .padding()
            .background(.red)
            .foregroundStyle(.white)
            .font(.largeTitle)
    }
}

struct FFCustomModifiers: View {
    var body: some View {
        Text("Hello, SwiftUI")
            .modifier(PrimaryLabel())
    }
}

调试结果

5、包装自定义UIView

尽管SwiftUI在提供许多UIKit的UIView子类方面做的很好,但目前还没有全部具备,可以为想要的UIView创建自定义包装器。为UITextView创建SwiftUI包装器作为副文本编辑器的基础,分为四个步骤:

  1. 创建符合UIViewRepresentable的结构体
  2. 定义一个属性来存储正在使用的文本字符串
  3. 给它一个makeUIView()方法,范围TextView
  4. 添加updateUIView()方法,每当TextView的数据发生更改时都会调用该方法。
Swift 复制代码
struct TextView: UIViewRepresentable {
    @Binding var text: NSMutableAttributedString
    
    func makeUIView(context: Context) -> some UIView {
        UITextView()
    }
    
    func updateUIView(_ uiView: UIViewType, context: Context) {
        uiView.accessibilityAttributedLabel = text
    }
}

struct FFCustomViewWrap: View {
    @State private var text = NSMutableAttributedString(string: "")
    var body: some View {
        //在SwiftUI视图中使用TextView组件
        TextView(text: $text)
            .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
    }
}

调试结果

6、如何为UIViewRepresentable结构创建修饰符

将UIView包装在UIViewRepresentable结构中是将现有UIKit引入SwiftUI工程的方式,甚至可以添加自定义修饰符来调整视图在运行时的状态。

要实现这个需求,应该要在底层UIView上调整的所有值创建私有属性,然后创建方法来调整它们,这些方法中的每一个都可以获取SwiftUI可表示副本(而不是底层UIView),然后调整自己创建的私有属性来更新状态。

完成后,SwiftUI将确保触发updateUIView()函数,此时你将私有属性复制到UIView中以确保更新。

SearchField

创建一个UIViewRepresentable将UISearchBar桥接到SwiftUI,但你可能希望它的某些方面是可定制的,例如她的占位符文本。首先,创建可表示对象,并为其占位符添加一个额外的私有属性。

Swift 复制代码
struct SearchField: UIViewRepresentable {
    @Binding var text: String
    private var placeholder = ""
    
    init(text: Binding<String>) {
        _text = text
    }
    
    func makeUIView(context: Context) -> some UIView {
        let searchBar = UISearchBar()
        searchBar.placeholder = placeholder
        return searchBar
    }
    
    func updateUIView(_ uiView: UIViewType, context: Context) {
        let view: UISearchBar = uiView as! UISearchBar
        view.text = text
        view.placeholder = placeholder
    }
}

创建修饰符调整私有属性

Swift 复制代码
extension SearchField {
    func placeholder(_ string: String) -> SearchField {
        var view = self
        view.placeholder = string
        return view
    }
}

使用自定义的修饰符

使用placeholder()修饰符创建了一个SearchField视图,但每次单击按钮时,会随机化占位符

Swift 复制代码
struct FFCreateModifiers: View {
    @State private var text = ""
    @State private var placeHolder = "Hello, wrold"
    var body: some View {
        VStack {
            SearchField(text: $text)
                .placeholder(placeHolder)
            //每次按下时随机更换占位符
            Button("Tap me") {
                placeHolder = UUID().uuidString
            }
        }
    }
}

调试结果

7、如何在文本中插入图像

SwiftUI可以使用"+"来组合文本视图,也可以使用简单的文本初始值设定项将图像直接放到文本中。

Swift 复制代码
struct FFTextInsertImage: View {
    var body: some View {
        //在Helloworld中添加一个星星icon
        Text("Hello ") + Text(Image(systemName: "star")) + Text(" World!")
        //文本中的图像将自动调整以匹配添加的修饰符(字体、颜色等),要用括号将链接的内容扩起来,以确保将修饰符应用于整个链接的文本。要不只能修饰最后一个Text
        (Text("Hello ") + Text(Image(systemName: "star")) + Text(" World!"))
            .font(.largeTitle)
            .foregroundStyle(.blue)
        //如果没有添加额外的括号,则只会修饰最后一个Text("World")
        Text("Hello ") + Text(Image(systemName: "star")) + Text(" World!")
            .font(.largeTitle)
            .foregroundStyle(.blue)
    }
}

调试结果

相关推荐
汉秋20 小时前
SwiftUI 中的 compositingGroup():真正含义与渲染原理
swiftui·swift
汉秋21 小时前
SwiftUI 中的 @ViewBuilder 全面解析
swiftui·swift
iOS阿玮1 天前
1V1 社交精准收割 3.6 亿!40 款马甲包 + 国内社交难度堪比史诗级!
uni-app·app·apple
胖虎11 天前
SwiftUI 页面作为一级页面数据被重置问题分析
ios·swiftui·swift·state·observedobject·stateobject·swiftui页面生命周期
guangzan2 天前
AI 结队编程:解决 SwiftUI 窗口点击关闭按钮崩溃问题
swiftui·tca
1024小神2 天前
xcode 配置了AppIcon 但是不显示icon图标
ios·swiftui·swift
iOS阿玮2 天前
想偷懒购买现成的应用,结果一更新就遇到了4.3a!
uni-app·app·apple
东坡肘子4 天前
周日小插曲 -- 肘子的 Swift 周报 #115
人工智能·swiftui·swift
YungFan5 天前
iOS开发之MetricKit监控App性能
ios·swiftui·swift
大熊猫侯佩5 天前
Swift 6.2 列传(第十二篇):杨不悔的“临终”不悔与 Isolated Deinit
swift·编程语言·apple