大师学SwiftUI第6章 - 声明式用户界面 Part 2

这篇具有很好参考价值的文章主要介绍了大师学SwiftUI第6章 - 声明式用户界面 Part 2。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

控制视图

控件是交互工具,用户通过交互修改界面状态、选取选项或插入、修改或删除信息。我们实现过其中的一部分,如前例中的​​Button​​​视图以及​​TextField​​视图。要定义一个有用的接口,需要学习有关视图的更多知识以及其它由SwiftUI所提供的控制视图。

按钮视图

我们已经学到,​​Button​​视图创建一个简单的控件,在点击时执行操作。以下是该结构体部分初始化方法。

  • Button(String, action: Closure):此初始化方法创建一个​​Button​​​视图。第一个参数是定义按钮标签的字符串,​​action​​参数是在点击按钮时执行的代码的闭包。
  • Button(action: Closure, label: Closure):此初始化方法创建一个​​Button​​​视图。​​action​​​参数是在点击按钮时执行的代码的闭包,​​label​​参数是返回用于创建标签的视图的闭包。
  • Button(String, role: ButtonRole?, action: Closure):此初始化方法创建一个​​Button​​​视图。第一个参数是定义按钮标签的字符串。​​role​​​参数一个结构体,包含描述按钮目的的类型属性。有两个属性:​​cancel​​​和​​destructive​​​。​​action​​参数是在点击按钮时执行的代码的闭包。

我们已经实现过第二个初始化方法创建按钮,但如果仅需对标签使用字符中,可以简化代码使用第一个初始方法加后置的用于操作的闭包。

示例6-10:实现​​Button​​视图

struct ContentView: View {
    @State private var colorActive: Bool = false
    
    var body: some View {
        VStack(spacing: 10) {
            Text("Default Title")
                .padding()
                .background(colorActive ? Color.green : Color.clear)
            Button("Change Color") {
                colorActive.toggle()
            }
            Spacer()
        }.padding()
    }
}

上例在​​VStack​​​中包仿一个​​Text​​​视图和一个​​Button​​​视图。​​Text​​​视图展示固定的文本,背景色用​​colorActive​​​属性定义。如果属性值是​​true​​​,我们将​​green​​​色赋值给背景,否则颜色为​​clear​​​(透明)。在按下按钮时,会切换这一属性的值,再次运算​​body​​属性的值,文本的背景修改为下一个颜色。

大师学SwiftUI第6章 - 声明式用户界面 Part 2,空间计算visionOS增强现实开发,swiftui,ui,apple vision pro,前端,ios

图6-4:按钮视图

✍️跟我一起做:创建一个多平台项目。使用示例6-10的代码更新​​ContentView​​视图。点击Change Color按钮。会看到文本背景色的变化(参见图6-4右图)。

如果希望将视图与控件执行的操作进行分离,可以将相关语句移到函数中。例如,可以上在​​ContentView​​​结构体中添加一个函数,用于切换​​colorActive​​属性的值,然后在按钮的操作中调用这个函数。应用的功能的相同,但代码更有条理。

示例6-11:使用函数来组织代码

struct ContentView: View {
    @State private var colorActive: Bool = false
    
    var body: some View {
        VStack(spacing: 10) {
            Text("Default Title")
                .padding()
                .background(colorActive ? Color.green : Color.clear)
            Button("Change Color") {
                changeColor()
            }
            Spacer()
        }.padding()
    }
    
    func changeColor() {
        colorActive.toggle()
    }
}

如果按钮唯一的操作就是调用方法,可以简化视图的定义为声明​​action​​参数并指定所要执行操作的方法名。如下所示。

示例6-12:引用方法

Button("Change Color", action: changeColor)

声明方法名称带括号会马上执行方法,但仅声明名称会提供一个方法的引用供系统稍后执行。

✍️跟我一起做:使用示例6-11中的代码更新​​ContentView​​视图。应用功能和之前相同。使用示例6-12中的​​Button​​​视图更新​​Button​​视图。点击按钮确定所执行的操作。

上例中,我们使用了三元运算符来根据​​colorActive​​​属性的值选取​​background()​​​修饰符的值。这是推荐的做法,这样SwiftUI可以识别视图并有效管理状态的转换,但我们也可以使用​​if else​​语句来响应修改。例如,有时会用按钮这类控件在界面中显示或隐藏视图。

示例6-13:在界面中添加及删除视图

struct ContentView: View {
    @State private var showInfo = false
    
    var body: some View {
        VStack(spacing: 10) {
            Button("Show Information") {
                showInfo.toggle()
            }.padding()
            if showInfo {
                Text("This is the information")
            }
            Spacer()
        }
    }
}

本例中的按钮切换​​@State​​​属性​​showInfo​​​的值。在按钮下方,可查看到该属性的当前值。若其值为​​true​​​,显示 ​​Text​​​视图,否则什么也不显示。因此,在按下按钮时,​​showInfo​​​的值发生改变,​​body​​​属性的内容会重新绘制,​​Text​​​视图根据​​showInfo​​的当前值出现或消失。

大师学SwiftUI第6章 - 声明式用户界面 Part 2,空间计算visionOS增强现实开发,swiftui,ui,apple vision pro,前端,ios

图6-5:动态界面

​if else​​语句可用于选择是否执行按钮的操作,但SwiftUI提供了如下修饰符来在要做禁用操作时禁用按钮。

  • disabled(Bool):这一修饰符决定该控件是否响应用户的交互。

下例使用了该修饰符在点击后禁用按钮,因此用户只能执行一次操作。

示例6-14:禁用按钮

struct ContentView: View {
    @State private var color = Color.clear
    @State private var buttonDisabled = false
    
    var body: some View {
        VStack(spacing: 10) {
            Text("Default Title")
                .padding()
                .background(color)
            Button("Change Color") {
                color = Color.green
                buttonDisabled = true
            }
            .disabled(buttonDisabled)
            Spacer()
        }.padding()
    }
}

这个视图包含两个​​@State​​​属性,一个用于追踪颜色,另一个表示按钮是否处于禁用状态。在点击按钮时,操作中将​​true​​​赋值给​​buttonDisabled​​属性,按钮就停止运作了,这样用户只能点一次。

大师学SwiftUI第6章 - 声明式用户界面 Part 2,空间计算visionOS增强现实开发,swiftui,ui,apple vision pro,前端,ios

图6-6:按钮禁用

和之前一样,​​Button​​​视图的初始化方法可以包含一个​​label​​​参数来定义所需视图的标签。这个参数非常灵活,可以包含像​​Text​​​视图和​​Image​​​视图的视图。按钮中的图片以原始渲染模式显示 ,也就是说以原始颜色显示,但还有一种模式可以创建带图片的蒙版,以应用的着重色或赋值给控件的前景色显示。为选取渲染模式,​​Image​​视图包含如下修饰符。

  • renderingMode(TemplateRenderingMode):这个修饰符对​​Image​​​视图定义了渲染械。参数是包含​​original​​​和​​template​​值的枚举。

下例定义了一个带图片和文本的按钮。将​​renderingMode()​​​修饰符应用于​​Image​​视图来以模板显示图片。

示例6-15:定义带图按钮的标签

struct ContentView: View {
    @State private var expanded: Bool = false
    
    var body: some View {
        VStack(spacing: 10) {
            Text("Default Title")
                .frame(minWidth: 0, maxWidth: expanded ? .infinity : 150, maxHeight: 50)
                .background(Color.yellow)
            Button(action: {
                expanded.toggle()
            }, label: {
                VStack {
                    Image(expanded ? .contract : .expand)
                        .renderingMode(.template)
                    Text(expanded ? "Contract" : "Expand")
                }
            })
            Spacer()
        }.padding()
    }
}

示例6-15中的视图包含一个​​@State​​​属性​​expanded​​​,用于控制​​Text​​​视图的宽度。如该属性的值为​​true​​​,我们使用​​infinity​​​值让宽度为最宽,否则,宽度为150点。每当用户点击按钮时,​​expanded​​​属性的值通过​​toggle()​​​方法进行切换,​​Text​​视图的宽度随之发生变化。

大师学SwiftUI第6章 - 声明式用户界面 Part 2,空间计算visionOS增强现实开发,swiftui,ui,apple vision pro,前端,ios

图6-7:带模板图片的按钮

✍️跟我一起做:下载expand.png和contract.png并添加至资源目录。使用示例6-15中的代码更新​​ContentView​​视图,点击Expand按钮。此时会看到图6-7中的界面。删除​​renderingMode()​​修饰符。我们应当会看到原色图。

可以通过如下修饰符对按钮赋标准样式。

  • buttonStyle(ButtonStyle):此修饰符定义了按钮的样式。参数是遵循​​ButtonStyle​​协议的一个结构体。
  • controlSize(ControlSize):此修饰符定义了按钮的样式。参数是一个枚举,值有​​large​​​、​​mini​​​、​​regular​​​和​​small​​。

SwiftUI框架自带有​​PrimitiveButtonStyle​​​协议提供标准样式。为此,该协议定义了类型属性​​automatic​​​、​​bordered​​​、​​borderedProminent​​​、​​borderless​​​和​​plain​​​。这些样式满足不同目的。例如,​​bordered​​​样式创建一个灰色背景的按钮,表示二级操作,​​borderedProminent​​样式创建一个应用着重色的按钮,表示主操作,比如用于保存或提交数据。例如以下视图包含两个按钮,一个取消处理,另一个将信息发送给服务端。

示例6-16:按钮样式

struct ContentView: View {
    var body: some View {
        VStack(spacing: 10) {
            HStack {
                Button("Cancel") {
                    print("Cancel Action")
                }.buttonStyle(.bordered)
                Spacer()
                Button("Send") {
                    print("Send Information")
                }.buttonStyle(.borderedProminent)
            }
            Spacer()
        }.padding()
    }
}

突出按钮应仅用于表示主操作。本例中,Cancel按钮加了边框,告诉用户这是一个二级操作,重要级为次级,但Send按钮为突出的,表示在点击该按钮时执行重要操作。

大师学SwiftUI第6章 - 声明式用户界面 Part 2,空间计算visionOS增强现实开发,swiftui,ui,apple vision pro,前端,ios

图6-8:标准样式按钮

按钮用于取消处理(如上例)或删除某一项时,我们可以通过​​Button​​​的初始化方法为其赋一个特定的角色。这样系统可以根据角色在应用运行的设备上对按钮添加样式。例如,在移动设备上,​​destructive​​角色的按钮以红色显示。

示例6-17:赋予角色

Button("Delete", role: .destructive) {
                    print("Delete Action")
                }.buttonStyle(.bordered)

大师学SwiftUI第6章 - 声明式用户界面 Part 2,空间计算visionOS增强现实开发,swiftui,ui,apple vision pro,前端,ios

图6-9:销毁按钮

✍️跟我一起做:使用示例6-16的代码更新​​ContentView​​视图。会看到如图6-8中所示的按钮。将Cancel按钮替换为示例6-17中的​​Button​​视图。会看到如图6-9中所示的删除按钮。

这些样式对SF图标进行了美化。SF图标替换普通图片的优势是它们会按对按钮添加的字体大小进行缩放。这配合对按钮自身进行缩放的​​controlSize()​​修饰符,使得我们可以创建不同大小的按钮。

示例6-18:缩放按钮

struct ContentView: View {
    var body: some View {
        VStack(spacing: 10) {
            Button(action: {
                print("Send information")
            }, label: {
                HStack {
                    Image(systemName: "mail")
                        .imageScale(.large)
                    Text("Send")
                }
            })
            .buttonStyle(.borderedProminent)
            .font(.largeTitle)
            .controlSize(.large)
            Spacer()
        }.padding()
    }
}

本例中,我们应用了​​imageScale()​​​修饰符来缩放SF图标,​​font()​​​修饰符对按钮添加了大字体,​​controlSize()​​修饰符对按钮进行缩放。结果如下。

大师学SwiftUI第6章 - 声明式用户界面 Part 2,空间计算visionOS增强现实开发,swiftui,ui,apple vision pro,前端,ios

图6-10:自定义大小的按钮

如果希望定义一个样式与系统自带的进行区分,则需要创建自己的​​ButtonStyle​​结构体。该协议只要求实现如下方法。

  • makeBody(configuration: Configuration):该方法定义并返回一个替换按钮体的视图。​​configuration​​​参数为包含按钮信息的​​Configuration​​类型的值。

该方法接收一个​​Configuration​​​类型的值,是​​ButtonStyleConfiguration​​的类型别名,包含返回按钮相关信息的属性。以下是其中的属性。

  • isPressed:该属性返回表示按钮是否按下的布尔值。
  • label:该属性返回定义按钮当前标签的一个或多个视图。

以下示例定义在点击时会放大的示例。样式包含一个内边距和绿色边框。要应用这些样式,必须创建一个符合​​ButtonStyle​​​协议的结构体,即实现​​makeBody()​​方法并通过该方法返回希望赋值给按钮体的视图。

组成按钮体的视图由​​Configuration​​​结构体的​​label​​属性提供,因此我们可以读取并修改这一属性值来应用新的样式,如下所示。

示例6-19:为按钮添加自定义样式

struct MyStyle: ButtonStyle {
    func makeBody(configuration: Configuration) -> some View {
        let pressed = configuration.isPressed
        return configuration.label
            .padding()
            .border(Color.green, width: 5)
            .scaleEffect(pressed ? 1.2 : 1.0)
    }
}

struct ContentView: View {
    @State private var color = Color.gray
    
    var body: some View {
        VStack(spacing: 10) {
            Text("Default title")
                .padding().foregroundColor(color)
            Button("Change Color") {
                color = Color.green
            }.buttonStyle(MyStyle())
            Spacer()
        }.padding()
    }
}

示例6-19中,我们定义了一个结构体​​MyStyle​​​并实现了所要求的​​makeBody()​​​方法。该方法通过类型属性获取到了按钮的当前配置,进而修改并返回标签。首先,我们读取​​isPressed​​​属性的值来了解按钮是否被按下,然后对​​label​​​属性应用新的样式。这个属性返回创建按钮当前标签的一个视图拷贝,然后通过修改其值我们也就修改了标签。本例中,我们应用了一个内边距、一个边框,然后根据​​isPressed​​​属性的值赋了一个缩放比例。如果该值为​​true​​​,也就是按钮被按下了,我们将比例设为1.2进行放大,但在值为​​false​​时,比例又回到了1。

在这一视图中,我们创建了该结构体的实例并通过​​buttonStyle()​​​修饰符将其值赋值给​​Button​​视图。如果如下所示。

大师学SwiftUI第6章 - 声明式用户界面 Part 2,空间计算visionOS增强现实开发,swiftui,ui,apple vision pro,前端,ios

图6-11:带自定义样式的按钮

✍️跟我一起做:使用示例6-19中的代码更新​​ContentView.swift​​文件。点击按钮。会看到按钮如图6-11右图那样放大了。SwiftUI自动对按钮添加了动画。我们会在第11章中学习自定义动画以及如何创建。

文本框视图

​TextField​​又是一个我们之前介绍过的控件。该视图创建一个输入框,用户可进行交互并插入值(单行文本)。以下是结构体中所包含的一个初始化方法。

  • TextField(String, text: Binding, axis: Axis):此初始化方法创建一个输入框。第一个参数定义该字段的占位符,​​text​​​参数是用于存储由用户所插入值的绑定属性,​​axis​​​参数定义在文本超出视图边界时沿哪条轴进行滚动。这是一个枚举,值有​​horizontal​​​和​​vertical​​。

框架为​​TextField​​视图定义了几个修饰符。以下是最常用的一些。

  • textFieldStyle(TextFieldStyle):此修饰符定义文本框的样式。参数是一个符合​​TextFieldStyle​​​协议的结构体。框架自带了几个提供标准样式的结构体。这些结构体定义了类型属性​​automatic​​​、​​plain​​​、​​roundedBorder​​​和​​squareBorder​​。
  • autocorrectionDisabled(Bool):此修饰符启用或禁用系统的自动修正特性。默认,该值为​​true​​(禁用状态)。
  • textInputAutocapitalization(TextInputAutocapitalization?):此修饰符定义用于格式化文本的大写样式。该参数是一个结构体,包含类型属性​​characters​​​、​​never​​​、​​, sentences (默认值)​​​和​​words​​。
  • keyboardType(UIKeyboardType):此修饰符定义待定输入框后系统打开的键盘类型。其参数是一个枚举,值有​​default​​​、​​asciiCapable​​​、​​numbersAndPunctuation​​​、​​URL​​​、​​numberPad​​​、​​phonePad​​​、​​namePhonePad​​​、​​emailAddress​​​、​​decimalPad​​​、​​twitter​​​、​​webSearch​​​、​​asciiCapableNumberPad​​​和​​alphabet​​。

我们已经学习如何包含一个简单的​​TextField​​视图来获取用户的输入,但只使用了少数几个修饰符。下例展示了如何对视图添加样式让单词变成大写。

示例6-20:配置文本框

struct ContentView: View {
    @State private var title: String = "Default Title"
    @State private var titleInput: String = ""
    
    var body: some View {
        VStack(spacing: 15) {
            Text(title)
                .lineLimit(1)
                .padding()
                .background(Color.yellow)
            TextField("Insert Title", text: $titleInput)
                .textFieldStyle(.roundedBorder)
                .textInputAutocapitalization(.words)
            Button("Save") {
                title = titleInput
                titleInput = ""
            }
            Spacer()
        }.padding()
    }
}

示例6-20中对​​TextField​​​视图应用的样式为​​roundedBorder​​。它为输入框添加一个边框,让视图占据的区域变得可见,如下所示。

大师学SwiftUI第6章 - 声明式用户界面 Part 2,空间计算visionOS增强现实开发,swiftui,ui,apple vision pro,前端,ios

图6-12:带圆角边框的文本框

✍️跟我一起做:使用示例6-20中的代码更新​​ContentView​​视图。在输入框中插入文本并按下Save按钮。会看到如图6-12所示的效果。

除了按钮,用户通过期望能够通过点击键盘上的Done按钮保存数据。为此框架提供了如下的修饰符。

  • onSubmit(of: SubmitTriggers, Closure):在发生触发条件(比如在键盘上按下Done/Return按钮时)时该修饰符执行一个操作。​​of​​​参数是指定修饰符所响应的触发条件类型的结构体。结构体中包含​​search​​​和​​text​​(默认)属性。第二个参数是希望执行的闭包。
  • submitLabel(SubmitLabel):该修饰符指定虚拟键盘中Done按钮所使用的标签。参数结构体包含的类型属性有​​continue​​​、​​done​​​、​​go​​​、​​join​​​、​​next​​​、​​return​​​、​​route​​​、​​search​​​和​​send​​。
  • submitScope(Bool):该修饰符指定在发生触发条件时是否提交视图。

赋值给​​onSubmit()​​​修饰符的闭包在聚焦于视图(例如用户编辑输入框)时执行。如果应用于​​TextField​​​视图,可省略​​of​​参数,如下例如下。

示例6-21:响应Done按钮

struct ContentView: View {
    @State private var title: String = "Default Title"
    @State private var titleInput: String = ""
    
    var body: some View {
        VStack(spacing: 15) {
            Text(title)
                .lineLimit(1)
                .padding()
                .background(Color.yellow)
            TextField("Insert Title", text: $titleInput)
                .textFieldStyle(.roundedBorder)
                .submitLabel(.continue)
                .onSubmit {
                    assignTitle()
                }
            HStack {
                Spacer()
                Button("Save") {
                    assignTitle()
                }
            }
            Spacer()
        }.padding()
    }
    func assignTitle() {
        title = titleInput
        titleInput = ""
    }
}

示例6-21中的代码实现了​​submitLabel()​​修饰符来修改Done按钮的标题为Continue,然后向结构体添加一个名为​​assignTitle()​​​的方法,执行和之前同样的操作。该方法在两处有调用,赋值给​​onSubmit()​​​修饰符的闭包和​​Button​​视图操作,因此在按下界面的按钮或点击键盘上的Done/Return按钮时执行该操作。不管用户决定执行什么操作,插入文本框的值总是存储于​​title​​属性中。

✍️跟我一起做:使用示例6-21中的代码更新​​ContentView​​结构体,并在iPhone模拟器上运行应用。点击输入框,插入文本并在键盘上点击Continue按钮。(若要在模拟器上启用虚拟键盘,打开I/O菜单,点击Keyword,选择Toggle Software Keyboard选项。)文本会像此前一样赋值给标题。

在视图可接收输入或处理用户选定的反馈时,我们就说视图聚焦了。SwiftUI包含了一些处理这种状态的工具。可以在视图获得焦点时处理某一任务、知道视图是否获得焦点或是从视图移除焦点。为此有两个属性包装器:​​@FocusState​​​和​​@FocusedBinding​​​。​​@FocusState​​​存储表明焦点当前存储在哪里的值,​​@FocusedBinding​​用于将状态传递给其它视图。为管理状态,框架内置了如下 修饰符。

  • focused(Binding, equals: Hashable):此修饰符将视图当前状态存储于绑定属性中。第一个参数是对​​@FocusState​​​属性的引用,​​equals​​参数是用于标识视图的可哈希值。
  • focusable(Bool):此标识符表示是否可将焦点放在视图上。

为追踪视图的状态,我们需要一个可哈希数据类型的​​@FocusState​​​属性,提供用于标识视图的值。下例中,属性通过枚举值进行创建。定义了两个值​​name​​​和​​surname​​,用于追踪两个输入框的聚焦状态,并在用户输入时修改背景色。

示例6-22:响应焦点中的变化

enum FocusName: Hashable {
    case name
    case surname
}

struct ContentView: View {
    @Environment(\.colorScheme) var colorScheme
    @FocusState var focusName: FocusName?
    @State private var title: String = "Default Name"
    @State private var nameInput: String = ""
    @State private var surnameInput: String = ""
    
    var body: some View {
        let color: Color = colorScheme == .dark ? .black : .white
        VStack(spacing: 10) {
            Text(title)
                .lineLimit(1)
                .padding()
                .background(Color.yellow)
            TextField("Insert Name", text: $nameInput)
                .textFieldStyle(.roundedBorder)
                .padding(4)
                .background(focusName == .name ? Color(white: 0.9) : color)
                .focused($focusName, equals: .name)
            TextField("Insert Surname", text: $surnameInput)
                .textFieldStyle(.roundedBorder)
                .padding(4)
                .background(focusName == .surname ? Color(white: 0.9) : color)
                .focused($focusName, equals: .surname)
            HStack {
                Spacer()
                Button("Save") {
                    title = nameInput + " " + surnameInput
                }
            }
            Spacer()
        }.padding()
    }
}

​@FocusState​​​属性的初始值是​​nil​​​,表示未聚焦于任何视图。在用户点击文本框时,焦点移至该视图,标识视图的值会被赋值给该属性。通过将该值与枚举中的值进行比较,我们就知道是哪个​​TextField​​​视图于聚焦状态,相应地修改背景色。注意​​roundedBorder​​样式对文本框添加了一个边框和白色背景,所以本例中只有边距的背景可见。

大师学SwiftUI第6章 - 声明式用户界面 Part 2,空间计算visionOS增强现实开发,swiftui,ui,apple vision pro,前端,ios

图6-13:聚焦

在移动设备中,在可处理输入的视图(如​​TextField​​​视图)获取到焦点时会打开虚拟键盘。只要焦点还在该视图上键盘就保持打开状态。也就是说要关闭键盘,我们必须移除该视图的焦点。在SwiftUI中通过对​​@FocusState​​​属性的赋值​​nil​​来实现,如下所示。

示例6-23:关闭键盘

Button("Save") {
                    title = nameInput + " " + surnameInput
                    focusName = nil
                }

示例6-22中的​​Button​​视图换成了示例6-23中的​​Button​​视图。现在,每当点击Save按钮时,会对值进行处理并关闭键盘。

✍️跟我一起做:使用示例6-22中的代码更新​​ContentView.swift​​文件并在iPhone模拟器上运行应用。点击输入框。背景会像图6-13那样变成灰色。使用示例6-23中的视图替换原​​Button​​视图。再次运行应用。在两个文本框中插入值并点击Save按钮。此时标题会被赋上新值,虚拟键盘关闭。

上例中,我们没有检测用户是否插入了值,但通常应用必须防止用户保存无效值或空值。有几种控制方式。一种是在存储之前就检测值。我们允许用户输入任意值,但仅保存应用所接受的值。

示例6-24:在存储前检测值

Button("Save") {
                    let tempName = nameInput.trimmingCharacters(in: .whitespaces)
                    let tempSurname = surnameInput.trimmingCharacters(in: .whitespaces)
                    
                    if !tempName.isEmpty && !tempSurname.isEmpty {
                        title = nameInput + " " + surnameInput
                        focusName = nil
                    }
                }

本例中,我们首先对​​nameInput​​​和​​surnameInput​​进行修剪去除其首尾的空格(参数第4章字符串一节),然后在将它们赋值给​​title​​属性之前检测结果值是否为空。Save按钮仍保持为激活状态,但仅在用户对两个字段都插入值时才执行保存。

✍️跟我一起做:使用示例6-24中的代码更新​​ContentView​​​视图中的​​Button​​视图。此时必须同时对名和姓两个字段插入值才能修改标题。

另一种方式是在用户插入的为非应用预期值时通过​​disabled()​​修饰符禁用按钮。

示例6-25:禁用按钮

Button("Save") {
                    let tempName = nameInput.trimmingCharacters(in: .whitespaces)
                    let tempSurname = surnameInput.trimmingCharacters(in: .whitespaces)
                    
                    if !tempName.isEmpty && !tempSurname.isEmpty {
                        title = nameInput + " " + surnameInput
                        focusName = nil
                    }
                }
            }.disabled(nameInput.isEmpty || surnameInput.isEmpty)

本例中,我们使用了前面介绍的​​disabled()​​修饰符来在用户在两个字段中输入文本前禁用按钮。如果其中一个或两个字段为空,按钮就无法使用。

✍️跟我一起做:使用示例6-25中的代码更新​​Button​​视图。只有同时插入名和姓时才能按下Save按钮。

除了可检测属性是否包含有效值,我们还能限定用户在字段中输入的内容。例如,我们可以只接受数字或指定数量的字符。这时,我们需要在每次视图状态发生改变时检测用户插入的值是否有效。框架为此内置了如下的修饰符。

  • onChange(of: State, initial: Bool, Closure):该修饰符在状态发生改变时执行闭包。​​of​​​参数是存储待检测值的属性,​​initial​​参数为指定在视图出现时是否还执行检测的布尔值,最后一个参数是在系统报出值发生改变时执行的闭包。闭包可接收两个值,一个表示属性的老值,另一个表示新值。

该修饰符只能检测一个状态,因此我们应对所有希望进行控制的视图应用该修饰符。例如,我们可以在示例中对那两个​​TextField​​视图使用它来限定允许用户输入的字符数。如果超出,会移除掉多余的字符将结果赋回属性,如下所示。

示例6-26:控制用户的输入

Text(title)
                .lineLimit(1)
                .padding()
                .background(Color.yellow)
            TextField("Insert Name", text: $nameInput)
                .textFieldStyle(.roundedBorder)
                .padding(4)
                .background(focusName == .name ? Color(white: 0.9) : color)
                .focused($focusName, equals: .name)
                .onChange(of: nameInput, initial: false) { old, value in
                    if value.count > 10 {
                        nameInput = String(value.prefix(10))
                    }
                }
            TextField("Insert Surname", text: $surnameInput)
                .textFieldStyle(.roundedBorder)
                .padding(4)
                .background(focusName == .surname ? Color(white: 0.9) : color)
                .focused($focusName, equals: .surname)
                .onChange(of: surnameInput, initial: false) { old, value in
                    if value.count > 15 {
                        surnameInput = String(value.prefix(15))
                    }
                }

示例6-26的代码中,我们检测存储文本框状态的属性的变化。在用户输入或删除字符时,相应的属性值发生改变,执行赋值给​​onChange()​​​修饰符的闭包。闭包接收属性的值。使用该值,我们检测用户插入的文本是否有效并进行相应的响应。在示例中,我们计算字符串中的字符数,如果值超出上限,我们使用​​prefix()​​方法从文本的开头进行截取,并将结果赋回给属性,这会更新视图并删除文本框中多余的字符。结果 是在字符数超出上限时,用户就无法输入更多的字符了。

✍️跟我一起做:使用示例6-26中的代码更新项目中的​​TextField​​视图。在iPhone模拟器中运行应用。插入名和姓。在名超过10个字符、姓超过15个字符时都无法再添加更多的字符。

当然,我们可以指定字符数外的其它条件。下例创建了一个仅接收整数数字的小应用。

示例6-27:仅接收整数数字

struct ContentView: View {
    @State private var title: String = "Default Name"
    @State private var numberInput = ""
    
    var body: some View {
        VStack(spacing: 10) {
            Text(title)
                .padding()
                .background(Color.yellow)
            TextField("Insert Number", text: $numberInput)
                .textFieldStyle(.roundedBorder)
                .padding(4)
                .keyboardType(.numbersAndPunctuation)
                .onChange(of: numberInput, initial: false) { old, value in
                    if !value.isEmpty && Int(value) == nil {
                        numberInput = old
                    }
                }
            HStack {
                Spacer()
                Button("Save") {
                    title = numberInput
                    numberInput = ""
                }
            }
            Spacer()
        }.padding()
    }
}

和之前一样,视图中包含一个带有​​onChange()​​​修饰符的​​TextField​​​。不同之处于在于如何对输入有进行有效性检测。本例中,我们需要确保文本框不为空,然后查看是否可以将其转化为整数,这表示用户只输入了数字。如果不能,就将闭包接收到的旧值赋值给​​numberInput​​属性,文本框回复到之前的状态。

注意我们还实现了​​keyboardType()​​修饰符来显示适配我们预期用户输入内容(本例为数字)的键盘。

✍️跟我一起做:使用示例6-27中的代码更新​​ContentView.swift​​文件。在iPhone模拟器上运行应用。此时只能输入数字。

默认,​​TextField​​​视图只显示一行文本,但我们可以使用​​lineLimit()​​​修饰符来允许视图进行扩展来包含更多的文本。(此前展开​​Text​​视图实现的同一个修饰符)。除了应用修饰符来设置我们所需的行数,我们还要告诉视图在纵轴上滚动内容,如下所示。

示例6-28:定义多行文本框

struct ContentView: View {
    @State private var text: String = ""
    
    var body: some View {
        TextField("Insert Text", text: $text, axis: .vertical)
            .textFieldStyle(.roundedBorder)
            .padding(20)
            .lineLimit(5)
    }
}

本例中,​​TextView​​​视图会进行扩展,直至到5行的高度时,然后会在垂直方向上滚动来允许用户持续输入。如若要对视图设置最小和最大尺寸,可以使用区间来声明修饰符,如​​lineLimit(3...5)​​。

大师学SwiftUI第6章 - 声明式用户界面 Part 2,空间计算visionOS增强现实开发,swiftui,ui,apple vision pro,前端,ios

图6-14:多行文本框

其它相关内容请见​​虚拟现实(VR)/增强现实(AR)&visionOS开发学习笔记​​文章来源地址https://www.toymoban.com/news/detail-801062.html

到了这里,关于大师学SwiftUI第6章 - 声明式用户界面 Part 2的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 用户界面设计和评估:如何设计具有吸引力、易用性和可靠性的用户界面?

    作者:禅与计算机程序设计艺术 用户界面(User Interface)是一个给用户提供服务或者产品的系统界面,通过人机交互、信息呈现、文字表达、图形符号等表现形式进行沟通,帮助用户更好的使用产品或服务。它对于提升企业的品牌影响力、增加客户黏性、改善用户体验、降低

    2024年02月13日
    浏览(39)
  • 前端用户体验设计:创造卓越的用户界面和交互

    💂 个人网站:【工具大全】【游戏大全】【神级源码资源网】 🤟 前端学习课程:👉【28个案例趣学前端】【400个JS面试题】 💅 寻找学习交流、摸鱼划水的小伙伴,请点击【摸鱼学习交流群】 用户体验(User Experience,UX)是前端开发中至关重要的一环。一个优秀的用户体验

    2024年02月04日
    浏览(42)
  • ImageJ 用户手册——第四部分(ImageJ用户界面)

    与大多数图像处理程序不同,ImageJ没有主工作区。ImageJ的主窗口实际上相当简洁,只包含一个菜单栏(位于Mac屏幕顶部),其中包含所有菜单命令、工具栏、状态栏和进度栏。图像、直方图、配置文件、窗口小部件等显示在附加窗口中。测量结果显示在结果表中。大多数窗口

    2024年02月10日
    浏览(44)
  • Matlab GUI界面美化:创建令人愉悦的用户界面

    Matlab GUI界面美化:创建令人愉悦的用户界面 在Matlab中,创建一个美观且易于使用的图形用户界面(GUI)对于增强用户体验至关重要。通过使用适当的颜色、布局和交互元素,可以使GUI更加吸引人,并且更易于导航和操作。本文将介绍一些简单而有效的方法,帮助您美化Matl

    2024年02月03日
    浏览(37)
  • TestStand-用户界面

    除序列编辑器外,TestStand自带的两类用户界面,分别是SimpleUI(简易用户界面)及Full-Featured UI(全功能用户界面)。 简易用户界面的源代码位于 TestStand安装路径UserInterfaces SimpleLabVIEW或者 TestStand PublicUserInterfaces SimpleLabVIEW目录下。 这两个目录中的内容是完全一样的,为了

    2024年02月14日
    浏览(40)
  • Python图形用户界面

    目录 1.图形用户界面概述 1.1tkinter 2.tkinter概述 2.1tkinter模块 2.2图形用户界面的构成  2.3框架和GUI应用程序类 2.4tkinter主窗口 3几何布局管理器 3.1pack几何布局管理器 3.2grid几何布局管理器 3.3place几何布局管理器 4.事件处理 4.1事件类型 4.2事件绑定 4.3事件处理函数 5.常用组件   

    2024年02月09日
    浏览(41)
  • java 图形用户界面

    目录 Swing与AWT概述 Swing概述——组件显示  框架与窗体 创建框架对象 框架Frame类结构  框架对象的创建及常用方法 创建Swing窗体对象 Swing窗体JFrame 类结构  Swing 窗体对象的创建  窗体对象常用属性 常用组件——文本组件 JLabel JTextField JPasswordField JTextArea​编辑  Jlist文本列表

    2024年02月06日
    浏览(46)
  • 界面开发(2)--- 使用PyQt5制作用户登陆界面

    上篇文章已经介绍了如何配置PyQt5环境,这篇文章在此基础上展开,主要记录一下如何使用 PyQt5 制作用户登陆界面,并对一些基础操作进行介绍。 下面是具体步骤,一起来看看吧! 1. 打开 Pycharm 中的 Qt Designer 工具。 2. 选择Main Window模式,创建界面窗口。 3. 移除菜单栏和状态

    2024年02月05日
    浏览(54)
  • python如何做出图形界面,用python做图形用户界面

    本篇文章给大家谈谈python如何做出图形界面,以及用python做图形用户界面,希望对各位有所帮助,不要忘了收藏本站喔。 图形用户界面(Graphical User Interface,GUI)是用户与程序交互的接口,好的GUI会大大提高用户交互体验,其实就是我们平时使用电脑时,使用鼠标、键盘点击

    2024年02月03日
    浏览(49)
  • 设计图形用户界面的原则

    1) 一般性原则:界面要具有一致性、常用操作要有快捷方式、 提供简单的错误处理、对操作人员的重要操作要有信息反馈、操作可 逆、设计良好的联机帮助、合理划分并高效地使用显示屏、保证信息 显示方式与数据输入方式的协调一致 2) 颜色的使用:颜色是一种有效的强化

    2024年02月08日
    浏览(64)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包