はじめに
前回の続きです。今回は、ボタン押下したタイミングで更新が表示される (※ロード中のぐるぐる) について記載していきます
今回も以下の動画の続きです。英語圏では素晴らしい情報が転がっていますね。英語の学習もしなければいけないとひしひしと感じています
参考動画
ソースコード
import SwiftUI
struct LoginView: View {
@State private var email: String = ""
@State private var password: String = ""
@State private var Isloading: Bool = false // ログインボタンを押下されたかの判定
var body: some View {
VStack(alignment:.leading , spacing: 10){
VStack(alignment:.leading , spacing: 8){
Text("ログインページです")
.font(.largeTitle)
Text("ログインフォームを表示します")
.font(.callout)
}
.fontWeight(.medium)
.padding(10)
CustomTextField(hint:"Eメール アドレス" , symbol : "mail" , value : $email)
.padding(.top, 10)
.padding(.horizontal,10)
CustomTextField(hint:"パスワード" , symbol : "key" , isPassword: true , value : $password)
.padding(.top, 10)
.padding(.horizontal,10)
// パスワードを忘れた際のボタンを記載
Button("パスワードを忘れましたか?") {
//content
}
.tint(.primary)
.frame(maxWidth: .infinity , alignment: .trailing)
.padding(.top , 10)
.padding(.horizontal , 10)
// Sign In ボタン
SignInView(title: "Sign In"){
try? await Task.sleep(for : .seconds(5))
} onStatusChange : { isLogin in
Isloading = isLogin // ログイン状態をIsloading に渡している
}
.padding(.top, 20)
.padding(.horizontal , 10)
.disabled(!isSignInButtonEnabled) // メアド・パスワードが入力されていないとアクティブにしない
HStack{
Text("アカウントをお持ちではないですか?")
Button{
} label: {
Text("Sign Up")
.underline()
}
}
.fontWeight(.medium)
.padding(.top, 10)
.foregroundStyle(Color.primary)
.frame(maxWidth: .infinity)
}
.frame(maxWidth: .infinity, maxHeight: .infinity , alignment: .topLeading)
.padding(.top, 20)
.allowsTightening(!Isloading)
.opacity(Isloading ? 0.8 : 1)
}
// メアド・パスワードが入力されていなかった場合は Sign In ボタンをアクティブにしない
var isSignInButtonEnabled: Bool {
!email.isEmpty && !password.isEmpty // only empty
// !email.isEmpty && password.count >= 8 // パスワードの文字列が8文字以上
}
}
struct SignInView: View {
var title : String
var onTask : () async -> () // 非同期で実行する処理を外から注入できるトリガー用の変数
var onStatusChange: (Bool) -> () = { _ in } // 実行しているかどうかの判定
@State private var isLogin: Bool = false
var body: some View {
Button {
Task{
isLogin = true
await onTask()
// 若干のスリープ処理
try? await Task.sleep(for: .seconds(0.1))
isLogin = false
}
} label: {
Text(title)
.font(.callout)
.fontWeight(.semibold)
.frame(maxWidth: .infinity)
.opacity(isLogin ? 0 : 1)
.overlay(
ProgressView()
.opacity(isLogin ? 1 : 0)
)
.padding(.vertical, 10)
}
.buttonStyle(.borderedProminent)
.animation(.easeInOut(duration: 0.1) , value: isLogin ) // isLogin が変わったときに、UI更新を0.25秒でアニメーションさせる
.buttonBorderShape(.capsule)
.tint(Color.primary)
.disabled(isLogin) // 引数のBool値が true のとき、そのViewを操作不能にする修飾子
.onChange(of: isLogin) { oldValue, newValue in
onStatusChange(newValue)
}
}
}
// 入力の欄
struct CustomTextField: View {
var hint: String
var symbol: String
var isPassword: Bool = false
@Binding var value: String
var body: some View {
HStack(spacing: 12) {
Image(systemName: symbol)
.font(.callout)
.foregroundStyle(.gray)
.frame(width: 30)
Group {
if isPassword {
SecureField(hint, text: $value) // ⚫️ 表示になる
} else {
TextField(hint, text: $value)
}
}
}
.padding(.horizontal, 15)
.padding(.vertical, 12)
.background(.ultraThinMaterial)
.clipShape(.capsule)
}
}
ログイン実行処理
ログイン処理
Isloading = isLogin にてログイン状態を渡している
このログイン状態は SignInView にて Sign In ボタンを押下した際に判定している (True にしている)
// Sign In ボタン
SignInView(title: "Sign In"){
try? await Task.sleep(for : .seconds(5))
} onStatusChange : { isLogin in
Isloading = isLogin // ログイン状態をIsloading に渡している
}
ログイン処理のパフォーマンス
Sign In ボタンが押下されたタイミングで、ログイン処理が実行されていることを認識しやすくするため一時的に処理を止める
try? await Task.sleep(for : .seconds(5))
ログイン時の UI 安定化
テキストの文字間の自動調整をオフにすることによるちらつき防止
.allowsTightening(!Isloading)
Sign In 次の画面の透明度の変更
Sign In ボタン押下した際に全体の画面の表示を薄くし、Sign In 中であると認識しやすくする
// 親View (LoginView) 全体の透明度を下げて Sign In 中は少し薄くする
.opacity(Isloading ? 0.8 : 1)
Sign In 中の Sign In ボタンの挙動について
Sign In ボタンを押下した際に、更新中の UI を表示する (ぐるぐる表記) Sign In の処理中ということを認識しやすくする
// ボタンを謳歌した際に更新中画面を表示する
.overlay(
ProgressView() // 更新画面 (ぐるぐる) の表示
.opacity(isLogin ? 1 : 0)
)
実際の画面
Sign In ボタン押下前
Sign In ボタン押下後
終わりに
今回はここまでです。だんだんと見慣れている画面に近づいてきましたね。次回は、アカウント作成画面についてまとめていきます
