0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【SwiftUI】ログイン認証について 更新中画面

Posted at

はじめに

前回の続きです。今回は、ボタン押下したタイミングで更新が表示される (※ロード中のぐるぐる) について記載していきます
今回も以下の動画の続きです。英語圏では素晴らしい情報が転がっていますね。英語の学習もしなければいけないとひしひしと感じています

参考動画

ソースコード

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 ボタン押下後

終わりに

今回はここまでです。だんだんと見慣れている画面に近づいてきましたね。次回は、アカウント作成画面についてまとめていきます

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?