大多数浏览器和
Developer App 均支持流媒体播放。
-
在 SwiftUI 中引导和反映焦点
使用设备输入,就像生活中的所有事一样,把焦点放在哪里很重要。探索如何使用 SwiftUI 在 app 中移动焦点、用程序自动关闭键盘以及从小视图构建大型导航目标。这些 API 结合起来可以帮助您简化 app 的界面,使其更强大,让人们可以找到需要的东西。
资源
相关视频
WWDC22
WWDC21
-
下载
♪低音音乐播放♪ ♪ 塔努辛格尔:大家好 我叫塔努 是Apple TV团队的 SwiftUI工程师 今天 我们将讨论 在SwiftUI中使用专注模式的一些新方法 SwiftUI的一大优点是 你在初次使用时 可以免费获得的功能 内置组件会根据其在 SwiftUI平台规则知识上的默认行为 从而产生在各种情况下 合理和熟悉的整体体验 当我们处于专注模式时 可以看到这种智能在发挥作用 专注模式是一个让你的app 从键盘、遥控器、游戏控制器 可访问的开关控制 以及其他来源接收输入的系统 与触摸输入不同 与特定的屏幕坐标无关 通常 专注模式视图是用 特殊的点缀绘制的 便于让人们预测 他们的输入将被引导到哪里 在大多数情况下 SwiftUI 会代表你管理专注模式 当有人单击文本字段 或按Tab键时 或者当有人点击 可调节手表的复杂功能 或在Siri Remote上滑动时 SwiftUI会决定专注模式如何受到影响 以及如何指示其下一个位置 在通过规则决定正确行为的 简单情况下非常有用 但是 在某些情况下 你可能希望在专注模式上获得更快的体验 在“备忘录”app的这个例子中 当我们选定新的备忘录按钮时 我们希望专注模式自动移动到 新创建的备忘录 这种类型的行为 当然需要自定义实现 在这里 我们有一个场景 我们想把专注模式 从左下角的按钮移动到 当用户在遥控器上向右滑动时 可以来到屏幕顶部附近的内容 由于音乐按钮和app磁贴 彼此不相邻 SwiftUI无法自动猜测 将专注模式移到何处 它需要更多信息 才能链接远距离专注模式目标 在这个iOS的例子中 我们只是想在用户 选择一个活动时 让键盘消失 幸亏有我们今年推出的新API 你现在可以在SwiftUI中 完成所有这些 在本次讲座中 我们将 重点关注两种用例 我们将看到如何在 没有任何直接输入的情况下 将专注模式移动到特定视图 我们将从小视图中制作大型导航目标 以便用户能触及任何内容 首先来看一个范例 我们可能希望移动专注模式 来帮助引导用户的注意力 我们已经准备好去度假了 我们已经开始开发 Vacation Planner app 该app适用于所有Apple平台 并允许你浏览目的地 以及预定行程 当你启动此app时 我们会显示一个电子邮件字段 一个密码字段和一个 “通过Apple登录”的按钮 这样你就可以登录你的帐户 如果我们发现输入的 电子邮件格式不正确 我们希望以编程方式 将专注模式移回电子邮件字段 为了实现这种行为 我们将使用FocusState API 我们登录视图的现有代码 包含一个VStack 它有一个TextField和SecureField 现在 我们将向该视图添加一个 FocusState属性包装器 FocusState 是我们今年推出的新API 这是一种特殊类型的状态 会根据当前专注模式位置而变化 我们将使用FocusState 来保存被聚焦的字段的标识符 FocusField变量是我们 为此范例创建的 枚举类型 你可以为FocusState使用字符串 整数或任何其他可散列值类型 请注意 FocusState值是可选择的 通常用于FocusState的类型 必须是可散列的和可选的 以及在nil应用于 专注模式位于屏幕不相关部分的情况 接下来 我们将向TextField 和SecureField添加专注模式修饰符 这也是今年新推出的 这个修改器创建了 在聚焦的位置之间的链接 以及FocusedField属性的值 这个简单链接是一个强大的工具 因为它意味着你可以 使用当前的聚焦位置 来在你的app中做出其他决定 我们可以在登录窗体中观看播放 当屏幕第一次出现时 没有聚焦于任何东西 所以FocusedField的值为nil 但是 如果有人随后点击 电子邮件文本字段 该字段将获得专注模式 同时键盘会出现 由于专注模式文本字段绑定了一个 FocusState值 FocusedField的值将会自动更新 来保存电子邮件文本字段的标识符 聚焦的位置和FocusState 之间的链接是双向的 这意味着我们不会只限于 对专注模式变化做出反应 我们可以通过更新我们的 FocusState属性 以编程方式移动专注模式 例如 如果我们以编程方式 将FocusedField的值 设置为.password SwiftUI会知道我们的SecureField 与我们正在设置的新值相关联 专注模式将自动移至密码字段 现在我们的专注模式绑定已经就位 我们可以让它们开始运作 在Vacation Planner app中 当用户提交他们的数据时 我们希望对其进行验证 如果电子邮件不是预期的格式 我们会将FocusedField 设置为电子邮件 这会将专注模式发送回电子邮件文本字段 如果它已经不在那里的话 此外 如果电子邮件无效 我们想用边框突出显示电子邮件字段 我们希望此边框 仅在专注模式位于电子邮件字段时出现 为此 我们可以在创建边框时 轻松读取FocusField的值 来看看这一切是如何结合在一起的 注意电子邮件字段没有有效地址 目前专注模式用在了在密码的字段上 当我们点击Go时 onSubmit回调 会在设置了FocusField的地方 受到触发 这会导致光标移回电子邮件字段 当电子邮件字段被聚焦时 我们会看到它周围的红色边框 然而 一旦我们将专注模式 从电子邮件字段移开 我们的FocusedField 就不再等于电子邮件标识符 所以红色边框消失了 在所有表单数据都有效的情况下 我们只想单纯地关闭键盘 要关闭键盘 我们将FocusState变量设置为nil 由于FocusedField是可选的 我们使用nil 表示专注模式已离开此视图 在视频中 请注意 电子邮件地址已更新 这一次当我们提交时 由于我们已将 FocusState 变量设置为nil 因此键盘就被关闭了 我们已经看到了 在我们的app有文本字段时 以编程方式控制专注模式是有帮助的 但是 FocusStates 不仅仅用于文本字段 它们可用于以编程方式控制 iOS、Apple tvOS、watchOS 或macOS上任何可专注模式视图 在下一个环节中 我们将探讨 基于专注模式的导航 在我们app中扮演的角色 让我们来看看我们的 Vacation Planner app的Apple tvOS版本 我们利用电视上的额外空间 通过添加来自某些 你可能想参观的景点照片 单击“浏览照片”按钮 你可以查看更多照片 甚至在登录之前就可以 请注意 专注模式最初位于 电子邮件字段上 如果我们在Siri Remote上向右滑动 我们希望专注模式移到 “浏览照片”按钮上 但是 默认情况下这不起作用 这是因为定向专注模式导航 是基于邻接关系的 当通过滑动来移动专注模式时 只有在 给定方向上有相邻且可聚焦的东西时 焦点才会移动 看看这个app中的可聚焦视图 因为没有可聚焦的视图 与左侧的登录字段相邻 底部的按钮无法访问 要使此屏幕能够导航 我们将扩展浏览按钮的可聚焦区域 因此它与登录字段相邻 这是使用新的 FocusSections API完成的 让我们来看看这有多容易 这里我们有一个简化版的 Vacation Planner在TV上的代码 它包含一个带有两个VStack的HStack 一个用于登录字段 另一个用于图像和按钮的VStack 我们想在按钮周围创建 一个更大的逻辑专注模式目标 以便专注模式可以表现得 就好像按钮与登录字段相邻一样 这可以通过向包含按钮的VStack 添加一个FocusSection修饰符来完成 在FocusSection修饰符 添加到任何视图时 该视图的框架就变得能够接受专注模式 如果它包含任何可聚焦的子视图的话 由于我们还希望在向左滑动按钮时 将专注模式移回登录字段 我们将向第一个VStack添加另一个 FocusSection修饰符 现在当我们运行这个app时 用户可以 通过在遥控器上左右滑动 在输入字段和浏览按钮之间移动专注模式 在讲座的最后 我鼓励你们关注一下专注模式 在不同平台上的差异 SwiftUI在大多数情况下 内置了很好的默认行为 新的专注模式状态和FocusSections API 可以帮助你利用专注模式 创造更精简的体验 当你在你的app上工作时 花点时间观察专注模式 影响用户行为的多种方式 我们希望本次讲座能协助你 帮助用户专注于最重要的事情 感谢收看 祝你们享受今年的WWDC ♪
-
-
3:38 - Slide 13 - Textfield and Securefield
import SwiftUI import AuthenticationServices struct ContentView: View { @State private var email: String = "" @State private var password: String = "" var body: some View { ZStack { Image("backgroundImage") .resizable() .opacity(0.7) .ignoresSafeArea() VStack(alignment: .center) { Text("Vacation Planner") .font(.custom("Baskerville-SemiBoldItalic", size: 60)) .foregroundColor(.black.opacity(0.8)) .frame(alignment: .top) Spacer(minLength: 30) TextField("Email", text: $email) .submitLabel(.next) .textContentType(.emailAddress) .keyboardType(.emailAddress) .padding() .frame(height: 50) .background(Color.white.opacity(0.9)) .cornerRadius(15) .padding(10) SecureField("Password", text: $password) .submitLabel(.go) .padding() .frame(height:50) .textContentType(.password) .background(Color.white.opacity(0.9)) .cornerRadius(15) .padding(10) Spacer().frame(height: 20) HStack { Rectangle().frame(height: 1) Text("or").bold().padding() Rectangle().frame(height: 1) } .foregroundColor(.black.opacity(0.7)) Spacer().frame(height: 20) SignInWithAppleButton(.signIn) { request in request.requestedScopes = [.fullName, .email] } onCompletion: { result in switch result { case .success (_): print("Authorization successful.") case .failure (let error): print("Authorization failed: " + error.localizedDescription) } } .frame(height: 50) .cornerRadius(15) Spacer().frame(height: 20) } .frame(width: 280, height: 500, alignment: .bottom) } } }
-
3:49 - Slide 14 - Focus State
import SwiftUI import AuthenticationServices struct ContentView: View { @FocusState private var focusedField: Field? @State private var email: String = "" @State private var password: String = "" var body: some View { ZStack { Image("backgroundImage") .resizable() .opacity(0.7) .ignoresSafeArea() VStack(alignment: .center) { Text("Vacation Planner") .font(.custom("Baskerville-SemiBoldItalic", size: 60)) .foregroundColor(.black.opacity(0.8)) .frame(alignment: .top) Spacer(minLength: 30) TextField("Email", text: $email) .submitLabel(.next) .textContentType(.emailAddress) .keyboardType(.emailAddress) .padding() .frame(height: 50) .background(Color.white.opacity(0.9)) .cornerRadius(15) .padding(10) SecureField("Password", text: $password) .submitLabel(.go) .padding() .frame(height:50) .textContentType(.password) .background(Color.white.opacity(0.9)) .cornerRadius(15) .padding(10) Spacer().frame(height: 20) HStack { Rectangle().frame(height: 1) Text("or").bold().padding() Rectangle().frame(height: 1) } .foregroundColor(.black.opacity(0.7)) Spacer().frame(height: 20) SignInWithAppleButton(.signIn) { request in request.requestedScopes = [.fullName, .email] } onCompletion: { result in switch result { case .success (_): print("Authorization successful.") case .failure (let error): print("Authorization failed: " + error.localizedDescription) } } .frame(height: 50) .cornerRadius(15) Spacer().frame(height: 20) } .frame(width: 280, height: 500, alignment: .bottom) } } }
-
4:07 - Slide 15 - Focus Field
import SwiftUI import AuthenticationServices enum Field: Hashable { case email case password } struct ContentView: View { @FocusState private var focusedField: Field? @State private var email: String = "" @State private var password: String = "" var body: some View { ZStack { Image("backgroundImage") .resizable() .opacity(0.7) .ignoresSafeArea() VStack(alignment: .center) { Text("Vacation Planner") .font(.custom("Baskerville-SemiBoldItalic", size: 60)) .foregroundColor(.black.opacity(0.8)) .frame(alignment: .top) Spacer(minLength: 30) TextField("Email", text: $email) .submitLabel(.next) .textContentType(.emailAddress) .keyboardType(.emailAddress) .padding() .frame(height: 50) .background(Color.white.opacity(0.9)) .cornerRadius(15) .padding(10) SecureField("Password", text: $password) .submitLabel(.go) .padding() .frame(height:50) .textContentType(.password) .background(Color.white.opacity(0.9)) .cornerRadius(15) .padding(10) Spacer().frame(height: 20) HStack { Rectangle().frame(height: 1) Text("or").bold().padding() Rectangle().frame(height: 1) } .foregroundColor(.black.opacity(0.7)) Spacer().frame(height: 20) SignInWithAppleButton(.signIn) { request in request.requestedScopes = [.fullName, .email] } onCompletion: { result in switch result { case .success (_): print("Authorization successful.") case .failure (let error): print("Authorization failed: " + error.localizedDescription) } } .frame(height: 50) .cornerRadius(15) Spacer().frame(height: 20) } .frame(width: 280, height: 500, alignment: .bottom) } } }
-
4:32 - Slide 17 - focused modifiers
import SwiftUI import AuthenticationServices enum Field: Hashable { case email case password } struct ContentView: View { @FocusState private var focusedField: Field? @State private var email: String = "" @State private var password: String = "" var body: some View { ZStack { Image("backgroundImage") .resizable() .opacity(0.7) .ignoresSafeArea() VStack(alignment: .center) { Text("Vacation Planner") .font(.custom("Baskerville-SemiBoldItalic", size: 60)) .foregroundColor(.black.opacity(0.8)) .frame(alignment: .top) Spacer(minLength: 30) TextField("Email", text: $email) .submitLabel(.next) .textContentType(.emailAddress) .keyboardType(.emailAddress) .padding() .frame(height: 50) .background(Color.white.opacity(0.9)) .cornerRadius(15) .padding(10) .focused($focusedField, equals: .email) SecureField("Password", text: $password) .submitLabel(.go) .padding() .frame(height:50) .textContentType(.password) .background(Color.white.opacity(0.9)) .cornerRadius(15) .padding(10) .focused($focusedField, equals: .password) Spacer().frame(height: 20) HStack { Rectangle().frame(height: 1) Text("or").bold().padding() Rectangle().frame(height: 1) } .foregroundColor(.black.opacity(0.7)) Spacer().frame(height: 20) SignInWithAppleButton(.signIn) { request in request.requestedScopes = [.fullName, .email] } onCompletion: { result in switch result { case .success (_): print("Authorization successful.") case .failure (let error): print("Authorization failed: " + error.localizedDescription) } } .frame(height: 50) .cornerRadius(15) Spacer().frame(height: 20) } .frame(width: 280, height: 500, alignment: .bottom) } } }
-
6:07 - Slide 25 - onSubmit
import SwiftUI import AuthenticationServices enum Field: Hashable { case email case password } struct ContentView: View { @FocusState private var focusedField: Field? @State private var email: String = "" @State private var password: String = "" @State private var submittedEmail: String = "" var body: some View { ZStack { Image("backgroundImage") .resizable() .opacity(0.7) .ignoresSafeArea() VStack(alignment: .center) { Text("Vacation Planner") .font(.custom("Baskerville-SemiBoldItalic", size: 60)) .foregroundColor(.black.opacity(0.8)) .frame(alignment: .top) Spacer(minLength: 30) TextField("Email", text: $email) .submitLabel(.next) .textContentType(.emailAddress) .keyboardType(.emailAddress) .padding() .frame(height: 50) .background(Color.white.opacity(0.9)) .cornerRadius(15) .padding(10) .focused($focusedField, equals: .email) SecureField("Password", text: $password) .submitLabel(.go) .padding() .frame(height:50) .textContentType(.password) .background(Color.white.opacity(0.9)) .cornerRadius(15) .padding(10) .focused($focusedField, equals: .password) Spacer().frame(height: 20) HStack { Rectangle().frame(height: 1) Text("or").bold().padding() Rectangle().frame(height: 1) } .foregroundColor(.black.opacity(0.7)) Spacer().frame(height: 20) SignInWithAppleButton(.signIn) { request in request.requestedScopes = [.fullName, .email] } onCompletion: { result in switch result { case .success (_): print("Authorization successful.") case .failure (let error): print("Authorization failed: " + error.localizedDescription) } } .frame(height: 50) .cornerRadius(15) Spacer().frame(height: 20) } .frame(width: 280, height: 500, alignment: .bottom) .onSubmit { submittedEmail = email if !isEmailValid { focusedField = .email } } } } private var isEmailValid : Bool { let regex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}" let predicate = NSPredicate(format:"SELF MATCHES %@", regex) return submittedEmail.isEmpty || predicate.evaluate(with: submittedEmail) } }
-
6:25 - Slide 26 - border
import SwiftUI import AuthenticationServices enum Field: Hashable { case email case password } struct ContentView: View { @FocusState private var focusedField: Field? @State private var email: String = "" @State private var password: String = "" @State private var submittedEmail: String = "" var body: some View { ZStack { Image("backgroundImage") .resizable() .opacity(0.7) .ignoresSafeArea() VStack(alignment: .center) { Text("Vacation Planner") .font(.custom("Baskerville-SemiBoldItalic", size: 60)) .foregroundColor(.black.opacity(0.8)) .frame(alignment: .top) Spacer(minLength: 30) TextField("Email", text: $email) .submitLabel(.next) .textContentType(.emailAddress) .keyboardType(.emailAddress) .padding() .frame(height: 50) .background(Color.white.opacity(0.9)) .cornerRadius(15) .padding(10) .focused($focusedField, equals: .email) .border(Color.red, width: (focusedField == .email && !isEmailValid) ? 2 : 0) SecureField("Password", text: $password) .submitLabel(.go) .padding() .frame(height:50) .textContentType(.password) .background(Color.white.opacity(0.9)) .cornerRadius(15) .padding(10) .focused($focusedField, equals: .password) Spacer().frame(height: 20) HStack { Rectangle().frame(height: 1) Text("or").bold().padding() Rectangle().frame(height: 1) } .foregroundColor(.black.opacity(0.7)) Spacer().frame(height: 20) SignInWithAppleButton(.signIn) { request in request.requestedScopes = [.fullName, .email] } onCompletion: { result in switch result { case .success (_): print("Authorization successful.") case .failure (let error): print("Authorization failed: " + error.localizedDescription) } } .frame(height: 50) .cornerRadius(15) Spacer().frame(height: 20) } .frame(width: 280, height: 500, alignment: .bottom) .onSubmit { submittedEmail = email if !isEmailValid { focusedField = .email } } } } private var isEmailValid : Bool { let regex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}" let predicate = NSPredicate(format:"SELF MATCHES %@", regex) return submittedEmail.isEmpty || predicate.evaluate(with: submittedEmail) } }
-
7:17 - Slide 29 - dismiss keyboard with nil
import SwiftUI import AuthenticationServices enum Field: Hashable { case email case password } struct ContentView: View { @FocusState private var focusedField: Field? @State private var email: String = "" @State private var password: String = "" @State private var submittedEmail: String = "" var body: some View { ZStack { Image("backgroundImage") .resizable() .opacity(0.7) .ignoresSafeArea() VStack(alignment: .center) { Text("Vacation Planner") .font(.custom("Baskerville-SemiBoldItalic", size: 60)) .foregroundColor(.black.opacity(0.8)) .frame(alignment: .top) Spacer(minLength: 30) TextField("Email", text: $email) .submitLabel(.next) .textContentType(.emailAddress) .keyboardType(.emailAddress) .padding() .frame(height: 50) .background(Color.white.opacity(0.9)) .cornerRadius(15) .padding(10) .focused($focusedField, equals: .email) .border(Color.red, width: (focusedField == .email && !isEmailValid) ? 2 : 0) SecureField("Password", text: $password) .submitLabel(.go) .padding() .frame(height:50) .textContentType(.password) .background(Color.white.opacity(0.9)) .cornerRadius(15) .padding(10) .focused($focusedField, equals: .password) Spacer().frame(height: 20) HStack { Rectangle().frame(height: 1) Text("or").bold().padding() Rectangle().frame(height: 1) } .foregroundColor(.black.opacity(0.7)) Spacer().frame(height: 20) SignInWithAppleButton(.signIn) { request in request.requestedScopes = [.fullName, .email] } onCompletion: { result in switch result { case .success (_): print("Authorization successful.") case .failure (let error): print("Authorization failed: " + error.localizedDescription) } } .frame(height: 50) .cornerRadius(15) Spacer().frame(height: 20) } .frame(width: 280, height: 500, alignment: .bottom) .onSubmit { submittedEmail = email if !isEmailValid { focusedField = .email } else { focusedField = nil // Show progress indicator, and log in. } } } } private var isEmailValid : Bool { let regex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}" let predicate = NSPredicate(format:"SELF MATCHES %@", regex) return submittedEmail.isEmpty || predicate.evaluate(with: submittedEmail) } }
-
9:24 - tv code
import SwiftUI import AuthenticationServices struct ContentView: View { @State private var email: String = "" @State private var password: String = "" var body: some View { HStack { VStack(alignment: .leading) { Spacer(minLength:60).frame(height: 150) Text("Vacation\nPlanner") .font(.custom("Baskerville-SemiBoldItalic", size: 60)) .foregroundColor(Color.black.opacity(0.8)) .lineLimit(nil) .multilineTextAlignment(.center) .padding(.horizontal, 40) Spacer().frame(height:80) TextField("Email", text: $email) .submitLabel(.next) .textContentType(.emailAddress) .keyboardType(.emailAddress) Spacer().frame(height:30) SecureField("Password", text: $password) .submitLabel(.go) .textContentType(.password) HStack { Rectangle().frame(height: 1) Text("or").bold().padding() Rectangle().frame(height: 1) } .foregroundColor(Color.black.opacity(0.7)) Spacer().frame(height: 20) SignInWithAppleButton(.signIn) { request in request.requestedScopes = [.fullName, .email] } onCompletion: { result in switch result { case .success (_): print("Authorization successful.") case .failure (let error): print("Authorization failed: " + error.localizedDescription) } } .frame(height: 50) Spacer() } .frame(width: 350, alignment: .center) VStack { Image(photoName) .resizable() .frame(width: 1400) .aspectRatio(contentMode: .fit) .ignoresSafeArea(edges: [.trailing]) BrowsePhotosButton() } }.preferredColorScheme(.light) } }
-
9:47 - focus section 1
import SwiftUI import AuthenticationServices struct ContentView: View { @State private var email: String = "" @State private var password: String = "" var body: some View { HStack { VStack(alignment: .leading) { Spacer(minLength:60).frame(height: 150) Text("Vacation\nPlanner") .font(.custom("Baskerville-SemiBoldItalic", size: 60)) .foregroundColor(Color.black.opacity(0.8)) .lineLimit(nil) .multilineTextAlignment(.center) .padding(.horizontal, 40) Spacer().frame(height:80) TextField("Email", text: $email) .submitLabel(.next) .textContentType(.emailAddress) .keyboardType(.emailAddress) Spacer().frame(height:30) SecureField("Password", text: $password) .submitLabel(.go) .textContentType(.password) HStack { Rectangle().frame(height: 1) Text("or").bold().padding() Rectangle().frame(height: 1) } .foregroundColor(Color.black.opacity(0.7)) Spacer().frame(height: 20) SignInWithAppleButton(.signIn) { request in request.requestedScopes = [.fullName, .email] } onCompletion: { result in switch result { case .success (_): print("Authorization successful.") case .failure (let error): print("Authorization failed: " + error.localizedDescription) } } .frame(height: 50) Spacer() } .frame(width: 350, alignment: .center) VStack { Image(photoName) .resizable() .frame(width: 1400) .aspectRatio(contentMode: .fit) .ignoresSafeArea(edges: [.trailing]) BrowsePhotosButton() } .focusSection() }.preferredColorScheme(.light) } }
-
10:06 - focus section 2
import SwiftUI import AuthenticationServices struct ContentView: View { @State private var email: String = "" @State private var password: String = "" var body: some View { HStack { VStack(alignment: .leading) { Spacer(minLength:60).frame(height: 150) Text("Vacation\nPlanner") .font(.custom("Baskerville-SemiBoldItalic", size: 60)) .foregroundColor(Color.black.opacity(0.8)) .lineLimit(nil) .multilineTextAlignment(.center) .padding(.horizontal, 40) Spacer().frame(height:80) TextField("Email", text: $email) .submitLabel(.next) .textContentType(.emailAddress) .keyboardType(.emailAddress) Spacer().frame(height:30) SecureField("Password", text: $password) .submitLabel(.go) .textContentType(.password) HStack { Rectangle().frame(height: 1) Text("or").bold().padding() Rectangle().frame(height: 1) } .foregroundColor(Color.black.opacity(0.7)) Spacer().frame(height: 20) SignInWithAppleButton(.signIn) { request in request.requestedScopes = [.fullName, .email] } onCompletion: { result in switch result { case .success (_): print("Authorization successful.") case .failure (let error): print("Authorization failed: " + error.localizedDescription) } } .frame(height: 50) Spacer() } .frame(width: 350, alignment: .center) .focusSection() VStack { Image(photoName) .resizable() .frame(width: 1400) .aspectRatio(contentMode: .fit) .ignoresSafeArea(edges: [.trailing]) BrowsePhotosButton() } .focusSection() }.preferredColorScheme(.light) } }
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。