본문 바로가기
ios 개발일지

[SwiftUI] 로그인 구현하기 #3 / UI 구현 - CustomCheckBox

by 리트레서 2024. 4. 12.
 

[SwiftUI] 로그인 구현하기 #2 / UI 구현 - TextField, Button

[SwiftUI] 로그인 구현하기 #1 / UI 구현 - Text, VStack Github Projects, issues, branch 연계해서 만들기 https://to-continually-grow.tistory.com/5 Xcode Branch 추가 및 Push / ios https://to-continually-grow.tistory.com/4 xcode github 연동

to-continually-grow.tistory.com

전 게시물에서 SwiftUI로 로그인 UI를 구현하는 중 textFieldButton 사용을 다루어봤습니다. 오늘은 CustomCheckBox를 구현해보겠습니다.

 


 

 

 

1. CustomCheckBox 구현

오늘은 로그인의 상태를 유지할 수 있는 체크박스를 추가하도록 하겠습니다.

우선 체크 박스가 눌려있는 모습을 나타내는 뷰와 체크 박스가 눌려있지 않을 때의 뷰를 만듭니다

// 체크박스가 눌렸을 때
ZStack(alignment: .center, content: {
	RoundedRectangle(
		cornerRadius: 5)
		.fill(Color(.lightGray))
		.frame(width: 20, height: 20)
})
            
// 체크박스가 눌리지 않았을 때
RoundedRectangle(
	cornerRadius: 5)
	.strokeBorder(lineWidth: 0.2)
	.foregroundStyle(Color.black)
	.frame(width: 20, height: 20)

눌렸을 때 / 눌리지 않았을 때

 

 

 

 

 

이후 체크박스 상태를 나타내는 Bool 타입의 변수를 선언하고 상태에 따른 뷰를 정의합니다.

@State private var isCheck = false
...

// 버튼을 누르면 체크박스의 상태를 관리하는 isCheck의 값이 변함
Button(action: {isCheck = !isCheck}, label: {
    // 체크박스가 눌렸을 때
    if(isCheck) {
        ZStack(alignment: .center, content: {
            RoundedRectangle(
                cornerRadius: 5)
                .fill(Color(.lightGray))
                .frame(width: 20, height: 20)
            Image(systemName: "checkmark").foregroundStyle(Color.white).font(.system(size: 12.5))
        })
    } 
    // 체크박스가 눌리지 않을 때
    else {
        RoundedRectangle(
            cornerRadius: 5)
            .strokeBorder(lineWidth: 0.2)
            .foregroundStyle(Color.black)
            .frame(width: 20, height: 20)
    }
})

 

 

 

 

 

이후 HStack체크박스의 용도를 설명하는 텍스트를 추가합니다.

// 로그인 상태 유지 체크박스
HStack(alignment: .center, content: {

    // 버튼을 누르면 체크박스의 상태를 관리하는 isCheck의 값이 변함
    Button(action: {isCheck = !isCheck}, label: {
        // 체크박스가 눌렸을 때
        if(isCheck) {
            ZStack(alignment: .center, content: {
                RoundedRectangle(
                    cornerRadius: 5)
                    .fill(Color(.lightGray))
                    .frame(width: 20, height: 20)
                Image(systemName: "checkmark").foregroundStyle(Color.white).font(.system(size: 12.5))
            })
        } 
        // 체크박스가 눌리지 않았을 때
        else {
            RoundedRectangle(
                cornerRadius: 5)
                .strokeBorder(lineWidth: 0.2)
                .foregroundStyle(Color.black)
                .frame(width: 20, height: 20)
        }
    })
                                  
    Text("로그인 상태 유지")
    Spacer()
    
}).frame(width: 280)

 

 

 

 

2. CustomCheckBox 구조화

아래와 같이 코드를 구성하면 체크 박스를 다른 스타일로 꾸미는데 어려움이 있습니다.

아래 코드를 구조화하여 좀 더 쉽게 체크 박스를 커스텀할 수 있게 만들어보겠습니다.

 

 

 

 

 

체크박스 구조를 정의했습니다. 

init()디폴트 매개변수를 정의하여 isCheck만 입력 받으면 체크박스가 생성되도록 구현하였습니다.

struct checkBoxView: View {
    @State var isCheck: Bool
    let checkColor: Color
    let backgroungColor: Color
    let checkSize : CGFloat
    let conerRadius: CGFloat
    let backgroundSize: CGFloat
    
    init(isCheck: Bool, checkColor: Color = Color.white, backgroundColor: Color = Color(.lightGray), checkSize: CGFloat = 12.5, conerRadius: CGFloat = 5, backgroundSize: CGFloat = 15) {
        self.isCheck = isCheck
        self.checkColor = checkColor
        self.backgroungColor = backgroundColor
        self.checkSize = checkSize
        self.conerRadius = conerRadius
        self.backgroundSize = backgroundSize
    }
    
    var body: some View {
        Button(action: {isCheck = !isCheck}, label: {
            if(isCheck) {
                ZStack(alignment: .center, content: {
                    RoundedRectangle(
                        cornerRadius: conerRadius)
                        .fill(backgroungColor)
                        .frame(width: backgroundSize, height: backgroundSize)
                    Image(systemName: "checkmark").foregroundStyle(checkColor).font(.system(size: checkSize))
                })
            } else {
                RoundedRectangle(
                    cornerRadius: conerRadius)
                    .strokeBorder(lineWidth: 0.2)
                    .foregroundStyle(Color.black)
                    .frame(width: backgroundSize, height: backgroundSize)
            }
        })
    }
}

- isCheck(required): 체크박스 상태

- checkColor(default = Color.white): 체크 색상

- backgroundColor(default = Color(.lightGray)): 체크박스 배경색

- checkSize(default = 12.5):  체크 사이즈

- conerRadius(default = 5): 체크박스 코너 둥글기

- backgroundSize(default = 15): 체크박스 배경 크기

 

 

 

 

 

아래와 같이 뷰를 호출하면 됩니다.

체크 박스 배경색을 바꾸고 싶으면 backgroundColor: Color를 쓰면 되고 다른 것을 바꾸고 싶으면 위에를 참고해서 바꾸면 됩니다.

checkBoxView(isCheck: true, checkSize: 17, backgroundSize: 20)

 

 

 

 

 

3. 전체 코드

import SwiftUI

struct ContentView: View {
    @State private var id: String = ""
    @State private var password: String = ""
    @State private var isCheck = false
    
    var body: some View {
        VStack {
            HStack {
                Text("안녕하세요\n0000입니다.")
                    .bold(true)
                    .font(.system(size: 25))
                     .multilineTextAlignment(.leading)
                     .padding(.bottom, 10)
                
                Spacer()
            }.frame(width: 280)
            
            HStack {
                Text("회원 서비스 이용을 위해 로그인 해주세요.")
                        .bold(true)
                        .font(.system(size: 16))
                        .multilineTextAlignment(.leading)
                        .padding(.bottom, 20)
                
                Spacer()
            }.frame(width: 280)
            
            // 이메일 입력
            TextField(
                "Email",
                text: $id
            )
            .padding(EdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 0))
            .textInputAutocapitalization(.never)
            .background(Color(red: 249/255, green: 245/255, blue: 244/255))
            .frame(width: 280, height: 40)
            .cornerRadius(7)

            // 비밀번호 입력
            SecureField(
                "Password",
                text: $password
            )
            .padding(EdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 0))
            .textInputAutocapitalization(.never)
            .background(Color(red: 249/255, green: 245/255, blue: 244/255))
            .frame(width: 280, height: 40)
            .cornerRadius(7)


            // 로그인 버튼
            Button(action: {}, label: {
                Text("Sign in")
                    .foregroundColor(.white)
                    .frame(width: 280, height: 40)
                    .background(Color.blue)
                    .cornerRadius(5)
            })
            HStack {
                checkBoxView(isCheck: true, checkSize: 17, backgroundSize: 20)
                Text("로그인 상태 유지")
                Spacer()
            }.frame(width: 280)
        }
    }
}

struct checkBoxView: View {
    @State var isCheck: Bool
    let checkColor: Color
    let backgroungColor: Color
    let checkSize : CGFloat
    let conerRadius: CGFloat
    let backgroundSize: CGFloat
    
    init(isCheck: Bool, checkColor: Color = Color.white, backgroundColor: Color = Color(.lightGray), checkSize: CGFloat = 12.5, conerRadius: CGFloat = 5, backgroundSize: CGFloat = 15) {
        self.isCheck = isCheck
        self.checkColor = checkColor
        self.backgroungColor = backgroundColor
        self.checkSize = checkSize
        self.conerRadius = conerRadius
        self.backgroundSize = backgroundSize
    }
    
    var body: some View {
        Button(action: {isCheck = !isCheck}, label: {
            if(isCheck) {
                ZStack(alignment: .center, content: {
                    RoundedRectangle(
                        cornerRadius: conerRadius)
                        .fill(backgroungColor)
                        .frame(width: backgroundSize, height: backgroundSize)
                    Image(systemName: "checkmark").foregroundStyle(checkColor).font(.system(size: checkSize))
                })
            } else {
                RoundedRectangle(
                    cornerRadius: conerRadius)
                    .strokeBorder(lineWidth: 0.2)
                    .foregroundStyle(Color.black)
                    .frame(width: backgroundSize, height: backgroundSize)
            }
        })
    }
}