-Preview-

SplashViewRankUpSystem

-프로젝트 진행상황-

  • SplashView 구현
  • CustomTabView 구현
  • SettingView 구현
  • HomeView 구현
  • AccountView 구현 / 프로필사진 선택
  • SwiftDataView 구현
  • DailyQuizView 구현 / 퀴즈 DB, 랜덤 퀴즈 구현 필요
  • RankSystem 구현

-금일 구현한 뷰-

SplashViewAccountView
DispatchQueue를 활용한 스플래쉬 뷰RankSystem 구현 및 닉네임 변경 구현

-ViewReview-

1. SplashView

스플래쉬뷰는 앱을 실행할 때 가장 먼저 보여지는 뷰이다.
스플래쉬뷰는 지난번 커피숍UI를 구현하면서 진행했던 적이 있었기 때문에 그 때와 동일한 방식인 DispatchQueue를 사용하여 구현하였다.

1. DispatchQueue로 스플래쉬 뷰 구현

// 스플래쉬뷰 노출 조건
@State private var splashOn: Bool = true

if self.splashOn {
	ParstaSplashView()
		.zIndex(2) // ZStack 내부에 있기 때문에 가장 앞에 오도록 인덱스 값 조정
		.onAppear(perform: {
        	// 3초 후 스플래쉬뷰가 사라지도록 설정
			DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
				withAnimation(.default) {
					self.splashOn = false
				}
			}
		})
}

2. 구현 결과물


2. AccountView

어카운트뷰는 사용자의 정보를 보여주는 뷰이다.
사용자의 현재 프로필 사진과 닉네임, 랭크, 경험치 상태를 보여주고 랭크업 조건을 별도로 표시해 주었다.
프로필사진의 변경 기능은 아직 구현 전으로 추후 구현에 도전할 예정이다.
닉네임이나 랭크, 경험치 등의 사용자 정보를 저장하는 코드로 UserDefaults를 사용하였다.

1. Rank타입 선언하기

먼저, 어느 조건을 달성해야 랭크업이 가능한지 사용자에게 보여주기 위한 안내를 작성하려고 한다.
이를 위해 먼저 Rank 타입의 구조체를 선언한다.

import Foundation
import SwiftUI

struct Rank: Hashable {
    var rankName: String
    var maxExp: Int
    var color: Color
}

// 만들어진 구조체를 활용하여 랭크 생성 및 배열 생성
let bronze: Rank = Rank(rankName: "Bronze", maxExp: 100000, color: Color.bronze)

let silver: Rank = Rank(rankName: "Silver", maxExp: 200000, color: Color.silver)

let gold: Rank = Rank(rankName: "Gold", maxExp: 300000, color: Color.gold)

let platinum: Rank = Rank(rankName: "Platinum", maxExp: 400000, color: Color.platinum)

let diamond: Rank = Rank(rankName: "Diamond", maxExp: 500000, color: Color.diamond)

let rankSet: [Rank] = [bronze, silver, gold, platinum, diamond]

2. Rank Information 작성하기

위에서 만든 Rank타입의 값들을 활용하여 랭크 안내서를 작성한다.
ForEach문을 사용하여 간편하게 작성하였다.

private let rank: [Rank] = rankSet

VStack(spacing: 20) {
	ForEach(rank, id: \.self) { rank in
		HStack(spacing: 0) {
			ZStack {     
				RoundedRectangle(cornerRadius: 20)
					.frame(width: 130, height: 50)
					.foregroundStyle(rank.color)
                        
				Text(rank.rankName)
					.font(.title2)
					.fontWeight(.bold)
					.foregroundStyle(Color.white)
				}
				.padding(.trailing, 15)

			VStack(alignment: .leading) {
				Text("레벨업까지 필요한 경험치:")
					.font(.headline)
					.fontWeight(.medium)
                        
				Text("\(rank.maxExp) exp")
					.font(.headline)
					.fontWeight(.black)
			}
		}
		.padding(.horizontal, 10)
	}
}

3. 닉네임 변경 구현하기

닉네임은 앱을 부팅했을 때 Sparta + 랜덤숫자 3자리로 지정되고, 이후 사용자가 임의로 변경할 수 있다.
닉네임은 빈 값으로 둘 수 없으며, 사용자가 재부팅 하더라도 지정했던 닉네임의 값이 유지되도록 UserDefaults를 사용하여 값을 저장하였다.

// 초기설정
@State private var userName: String = checkNickName()
@State private var editNickName: String = ""
@State private var edit: Bool = false
@State private var blankUserName: Bool = false

// 유저의 이름이 비어있는지 확인
func checkNickName() -> String {
    let check = UserDefaults.standard.string(forKey: "nickname")
    var nickName: String = ""
    guard check != nil else {
        return nickName
    }
    nickName = check!
    
    return nickName
}
HStack {
	// 닉네임 수정을 진행할 때
	if self.edit {
		TextField("Edit your Nickname", text: $editNickName)
			.textFieldStyle(.roundedBorder)
			.frame(width: 200)
                        
		Image(systemName: "checkmark.circle.fill")
			.font(.title2)
			.foregroundStyle(Color.parsta)
			.onTapGesture {
				guard self.editNickName.count > 0 else {
					self.blankUserName = true
					return
				}        
				UserDefaults.standard.set(self.editNickName, forKey: "nickname")
				self.userName = self.editNickName 
                
				withAnimation {
					self.edit.toggle()
				}
			}
        // 닉네임 수정을 하지 않을 때(기본)
		} else {
			Text(self.userName)
				.font(.title2)
				.fontWeight(.semibold)
                        
			Image(systemName: "pencil.circle.fill")
				.font(.title2)
				.foregroundStyle(Color.parstaGray)
				.onTapGesture {
					self.editNickName = ""
					withAnimation {
						self.edit.toggle()
					}
				}
			}
		}
        // 닉네임이 빈 값일 때 alert로 경고 메세지
		.alert(isPresented: $blankUserName) {
			.init(title: Text("Error!!"), message: Text("Nickname cannot be an empty value."), dismissButton: .default(Text("OK")))
		}
}

4. 랭크업, 경험치 구현

사용자의 현재 랭크에 따라 레벨업에 필요한 경험치량을 바꾸고, 필요 경험치를 충족하면 경험치가 초기화되며 랭크업을 하는 시스템을 구현한다.
사용자의 랭크 및 경험치는 UserDefaults로 저장 및 불러오기를 하였다.

// 초기설정
@State private var progress: CGFloat = 0 // 경험치바의 기본 상태값
@State private var isColor: Color = .bronze // 경험치바의 기본 색상
@State private var rank = UserDefaults.standard.string(forKey: "rank") // 사용자의 현재 랭크 불러오기
private let exp = UserDefaults.standard.integer(forKey: "exp") // 사용자의 현재 경험치 상태 불러오기

UserDefaults로 데이터를 불러오고 이를 코드에 적용하기 위해 여러 함수를 만들어 활용하였다.

// 경험치바의 값 생성
func progressChanger() -> CGFloat {
    let exp = UserDefaults.standard.integer(forKey: "exp")
    var progressValue: CGFloat = 0
    
    guard exp != 0 else {
        return progressValue
    }
    progressValue = CGFloat(exp)
    progressValue = progressValue / CGFloat(maxExpChange())
    
    return progressValue
}

// 사용자의 랭크에 따른 색 변경
func rankColorChange() -> Color {
    let level = UserDefaults.standard.integer(forKey: "level")
    
    switch level {
    case 0:
        return .bronze
    case 1:
        return .silver
    case 2:
        return .gold
    case 3:
        return .platinum
    case 4:
        return .diamond
    default:
        return .clear
    }
}

// 사용자의 랭크에 따른 필요 경험치량 변경
func maxExpChange() -> Int {
    let level = UserDefaults.standard.integer(forKey: "level")
    
    switch level {
    case 0:
        return 100000
    case 1:
        return 200000
    case 2:
        return 300000
    case 3:
        return 400000
    case 4:
        return 500000
    default:
        return 0
    }
}

// 사용자의 랭크에 따른 랭크명 변경
func rankNameChange() -> String {
    let level = UserDefaults.standard.integer(forKey: "level")
    
    switch level {
    case 0:
        return "Bronze"
    case 1:
        return "Silver"
    case 2:
        return "Gold"
    case 3:
        return "Platinum"
    case 4:
        return "Diamond"
    default:
        return ""
    }
}

// 사용자의 경험치량에 따른 랭크업 구현
func levelUp() {
    var level = UserDefaults.standard.integer(forKey: "level")
    var exp = UserDefaults.standard.integer(forKey: "exp")
    
    switch level {
    case 0:
        if exp >= 100000 {
            level += 1
            exp = 0
            UserDefaults.standard.set(exp, forKey: "exp")
            UserDefaults.standard.set(level, forKey: "level")
        } else {
            return
        }
    case 1:
        if exp >= 200000 {
            level += 1
            exp = 0
            UserDefaults.standard.set(exp, forKey: "exp")
            UserDefaults.standard.set(level, forKey: "level")
        } else {
            return
        }
    case 2:
        if exp >= 300000 {
            level += 1
            exp = 0
            UserDefaults.standard.set(exp, forKey: "exp")
            UserDefaults.standard.set(level, forKey: "level")
        } else {
            return
        }
    case 3:
        if exp >= 400000 {
            level += 1
            exp = 0
            UserDefaults.standard.set(exp, forKey: "exp")
            UserDefaults.standard.set(level, forKey: "level")
        } else {
            return
        }
    case 4:
        if exp >= 500000 {
            exp = 500000
            UserDefaults.standard.set(exp, forKey: "exp")
        } else {
            return
        }
    default:
        level = 0
    }
    
}

앱을 최초 실행시 UserDefaults에 값이 없어 문제가 발생할 수 있기 때문에 onAppear를 통해 UserDefaults의 값이 빈 값인 경우 새로운 값을 추가하도록 한다.

.onAppear() {
	// 유저의 닉네임 값 확인
	if UserDefaults.standard.string(forKey: "nickname") == nil {
		UserDefaults.standard.set("Sparta0\(Int.random(in: 1...99))", forKey: "nickname")
	}
    
    // 유저의 경험치 값 확인
	if UserDefaults.standard.integer(forKey: "exp") <= 0 {
		UserDefaults.standard.set(0, forKey: "exp")
	}
    
    // 유저의 랭크 확인
	if UserDefaults.standard.string(forKey: "rank") == nil {
		UserDefaults.standard.set("Bronze", forKey: "rank")
	}
    
    // 유저의 레벨 확인
	if UserDefaults.standard.integer(forKey: "level") <= 0 {
		UserDefaults.standard.set(0, forKey: "level")
	}

5. 구현 결과물

닉네임 변경
경험치 및 랭크업

-오늘의 학습후기-

오늘은 UserDefaults를 활용하여 경험치나 닉네임 등 다양한 작업을 진행해보았다.
닉네임을 바꾸고 저장하는 것은 크게 어렵지 않았지만, 랭크나 경험치 상태 등을 표시하고, 저장하고, 불러오는 것을 구현하는게 어려웠다.
수학적인 계산이 필요하기도 했고, 아직 UserDefaults의 사용이 미숙한 탓에 코드가 복잡해진 것 같다.
이번에는 함수를 주로 이용하여 문제를 해결했는데, 다음번엔 좀더 간결하고 깔끔한 코드를 작성하고 싶다.
남은 것은 퀴즈뷰의 구현과 프로필사진을 선택할 수 있도록 하는 것인데, 둘 다 한번도 시도해본적 없는 내용들이라 걱정이다.
profile
이유있는 코드를 쓰자!!

0개의 댓글