ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
SwiftUIのダイレクトフォーカスとリフレクションフォーカス
デバイス入力では、人生のあらゆることと同様に、どこにフォーカスを置くかが重要です。SwiftUIでApp内のフォーカスを移動したり、キーボードをプログラム的に解除したり、小さなビューから大きなナビゲーションターゲットを構築する方法を確認します。同時に、これらのAPIは、Appのインターフェイスをシンプル化し、ユーザが必要なものをより強力に見つけられるようにするのに役立ちます。
リソース
関連ビデオ
WWDC22
WWDC21
-
ダウンロード
♪ (SwiftUIのダイレクトフォーカスと リフレクションフォーカス) 皆さん こんにちは Apple TVチーム SwiftUIエンジニアの Tanuです 今日は SwiftUIでフォーカスを使う 新しい方法について ご紹介します SwiftUIの素晴らしい点の1つは 沢山の機能を無料でしかも使い始めてからすぐに 活用できることです 内蔵コンポーネントのデフォルト動作は 全プラットフォームのSwiftUIの知見に基づく 多様なコンテキストに適した 馴染みやすい全体体験につながります このインテリジェンスはフォーカスにもあります フォーカスとは キーボードやリモコン ゲームコントローラー アクセシブル・スイッチなどから Appが入力を取得するためのシステムで タッチ入力とは異なり 特定の画面座標に紐づけられていません フォーカスされたビューには 通常特別な装飾が施され 入力内容がどこに送られるか 予測しやすくします SwiftUIは大部分で あなたに代わってフォーカスを管理します 誰かがテキストフィールドでクリックしたり タブキーを押したり Watch Compilationのタップや Siri Remoteでスワイプすると SwiftUIはフォーカスの適用場所と 次の選択肢を決定します これは適切な動作が 慣例で決定できる場合など単純なケース向きです しかし フォーカス経験をもっと加速したい というケースもありますよね メモAppの例では 新しいメモボタンのタップだけで フォーカスが新しいメモに自動的に 移動するようにしたいです この種の動作は絶対にカスタム実装が必要です こちらのシナリオではユーザーがSiriリモートで 右へとスワイプした時に 左下側のボタンから画面上部の近くの コンテンツに移動するようにしたいです ミュージックボタンとAppタイルが 隣り合っていないので SwiftUIはフォーカス先を自動で推測できません 遠くのフォーカスターゲットと リンクするためにもっと情報が必要です iOSのこの例では ユーザーがイベントを選択した時 キーボードが 隠れるようにしたいです 今年導入した新しいAPIのおかげで これは全てSwiftUIから行えます この動画では2種類のユースケースに注目します 直接の入力なしに特定のビューへ フォーカスを移動する方法を学び 大きなナビゲーションターゲットを 小さなビューから作成し ユーザーが全てを操作できるようにします まず フォーカスを移動して ユーザーの注意を誘導したい 場合の例を見ていきましょう 夏休みのお出かけ予定を立てるため Vacation Planner Appを作るとします このAppは全てのAppleプラットフォームで使え 目的地の閲覧や 旅行に必要な予約ができます このAppを起動するとメールアドレス欄や パスワード欄 Appleでサインインが表示され アカウントにログインすることができます このログイン画面でメールアドレスが正しい フォーマットで入力されていない時 フォーカスをプログラムで メールアドレス欄に動かしたいです この動作を行うには FocusState APIを使います ログインビューの既存コードには TextField と SecureField がある VStackが含まれています このビューをFocusState property wrapperに 追加します FocusStateは今年導入の新しいAPIで 現在のフォーカスの場所に応じて 変動するタイプの特殊なAPIです FocusStateを使ってフォーカスされた欄の 識別子を置きます focusedField変数はこの例で作成した 列挙型のものです FocusStateには文字列や整数 その他 ハッシュ可能な値型が使えます FocusState値は任意のものです 通常 FocusStateに使われるタイプは hashableでoptionalであり フォーカスが画面の無関係な場所にある場合は ケースにnilがなければなりません 次にfocused修飾子を TextFieldとSecureFieldに加えます また 今年からはこの修飾子で フォーカス配置とfocusedFieldプロパティの間に リンクを作成することができます このシンプルなリンクはパワフルなツールです Appで他の決定をするための フォーカスの現在の配置を使えるからです ログインフォームで展開されているのが分かります まず画面が出てきましたがフォーカスがないので focusedFieldの値はnilです メールテキスト欄がタップされると 欄にフォーカスが得られキーボードが出てきます フォーカスされたテキスト欄は focusState値にリンクしており メールテキスト欄の識別子に 更新されます フォーカス配置とfocusStateのリンクは 諸刃の剣です つまりフォーカス変更の リアクションは無制限です focusStateプロパティを更新するだけで フォーカスをプログラム的に移動できます 例えばfocusedFieldの値を.passwordに プログラム的に変更する場合 SwiftUIはSecureFieldが 設定中の新しい値に関連付けられていて フォーカスが自動的に パスワード欄に移動すると知ります さてこれでフォーカスの紐づけができたので 早速動かしてみましょう Vacation Plannerでは ユーザーがデータを送信したら それを認証するのですが メールアドレスが正しく入力されていない場合 FocusedFieldをメールアドレスに設定します まだフォーカスされていない場合 メールアドレステキスト欄に フォーカスを設定します さらに メールアドレスが不正だった場合 アドレス欄を強調したいとします 縁取りはフォーカスが アドレス欄にある時のみ表示させたいです そのためには枠線作成時に focusedFieldの値をチェックします 最終的にどうなったか見てみましょう アドレス欄に正しいアドレスが入力されていません フォーカスは現在パスワード欄にあります goを押すとFocusedFieldで設定された onSubmitコールバックがトリガーされます これでカーソルがアドレス欄に戻ります アドレス欄がフォーカスされている時 赤い枠線が見えます しかし アドレス欄からフォーカスを動かすと focusedFieldがアドレス識別子と一致しなくなり そのため赤い枠線が消えます 全フィールドが有効になったら キーボードを隠したいです キーボードを消すには FocusState変数をnilに設定します focusedFieldはoptionalなので フォーカスがこのビューから無くなった事を nilで表します メールアドレス欄が更新されていますね 送信を押すとキーボードが消えます FocusState変数をnilに設定したためです テキスト欄のあるAppで フォーカスをプログラム的に管理するのは 便利だと説明してきましたが FocusStateはテキスト欄以外でも使えます iOS tvOS watchOS macOSでフォーカス可能な ビューのフォーカスを プログラムで管理できます 次のセクションではApp内のフォーカス型 ナビゲーションの役割を解説します Vacation PlannerのtvOSバージョンを 見てみましょう 行ってみたい目的地の 画像を追加してTVの 余分なスペースを活用しました Browse Photo のボタンをクリックするとログインする前から 画像をもっと見られます フォーカスが元はアドレス欄に あったことに注目します Siri Remoteで右にスワイプすると フォーカスが Browse Photoボタンに移動すると期待しますが デフォルト状態ではそうなりません これはフォーカスナビゲーションの順番が 隣接関係に基づくものだからです スワイプしてフォーカスを動かす時 その方向の隣にフォーカス可能な 対象がある時のみ移動します フォーカス可能なビューを見てみましょう 左側のログイン欄の隣には フォーカス可能なビューがないので 下ボタンはクリックできないようになっています この画面をナビゲート可能にするには Browseボタンの フォーカス可能エリアを拡大すると ログイン欄の隣ということになります これは新しいFocusSections APIから設定できます これはとても簡単です TV用のVacation Plannerコードを 単純化したものがあります これには2つのVStackを持つHStackが含まれます 1つはログイン欄用 そして画像やボタン用VStackです。 もっと大きな仮想フォーカスターゲットを ブラウズボタンの周りに作り ログイン欄の隣になるようにして フォーカスが移動できるようにします これはfocusSection修飾子をボタンを含む VStackに追加するだけで完了できます focusSection修飾子がビューに追加されると フォーカス可能なサブビューが含まれている場合 指定した親ビューのフレームで フォーカスを受け入れられるようになります ボタンで左にスワイプした時にフォーカスを ログイン欄に戻すようにもしたいので 最初のVStackに もう1つのfosucSection()を追加します これで Appを実行するとリモコンで左から右に スワイプすることで 入力欄とブラウズボタンの間で フォーカスを移動できるようになりました まとめると フォーカスにもっと注目してください プラットフォーム毎に違って見えるからです SwiftUIには ほとんどのケースで有効な 内蔵された素晴らしいデフォルト動作があります 新しいFocusStateとFocusSection 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) } }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。