Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import Foundation

/// 현재 로그인된 유저의 정보를 조회 (Response)
struct LoginUserModel: ResponseModelType {
struct LoginUserModel: ResponseModelType, Equatable {
let userID: Int
let name: String?
let level: Int
Expand Down
39 changes: 39 additions & 0 deletions KkuMulKum/Resource/Extension/Font+SwiftUI.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//
// Font+SwiftUI.swift
// KkuMulKum
//
// Created by SwiftUI Migration on 2025/01/08.
//

import SwiftUI

extension Font {
static func pretendard(_ style: UIFont.Pretendard) -> Font {
return Font.custom(style.weight, size: style.size)
}

// Convenience methods for common styles
static var pretendardTitle00: Font { pretendard(.title00) }
static var pretendardTitle01: Font { pretendard(.title01) }
static var pretendardTitle02: Font { pretendard(.title02) }
static var pretendardHead01: Font { pretendard(.head01) }
static var pretendardHead02: Font { pretendard(.head02) }
static var pretendardBody01: Font { pretendard(.body01) }
static var pretendardBody02: Font { pretendard(.body02) }
static var pretendardBody03: Font { pretendard(.body03) }
static var pretendardBody04: Font { pretendard(.body04) }
static var pretendardBody05: Font { pretendard(.body05) }
static var pretendardBody06: Font { pretendard(.body06) }
static var pretendardCaption01: Font { pretendard(.caption01) }
static var pretendardCaption02: Font { pretendard(.caption02) }
static var pretendardLabel00: Font { pretendard(.label00) }
static var pretendardLabel01: Font { pretendard(.label01) }
static var pretendardLabel02: Font { pretendard(.label02) }
}

// SwiftUI Color extension for UIKit colors
extension Color {
init(uiColor: UIColor) {
self.init(uiColor)
}
}
146 changes: 146 additions & 0 deletions KkuMulKum/Source/MyPage/View/MyPageContentSwiftUIView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
//
// MyPageContentSwiftUIView.swift
// KkuMulKum
//
// Created by SwiftUI Migration on 2025/01/08.
//

import SwiftUI
import Combine
import RxSwift

struct MyPageContentSwiftUIView: View {
@StateObject private var viewModelWrapper: MyPageViewModelWrapper
@State private var profileImage: UIImage? = UIImage.imgProfile

init(viewModel: MyPageViewModel) {
_viewModelWrapper = StateObject(wrappedValue: MyPageViewModelWrapper(viewModel: viewModel))
}

var body: some View {
ZStack {
Color(UIColor.green1)
.ignoresSafeArea()

VStack(spacing: 0) {
// Profile Stack - 높이 120으로 고정
VStack(spacing: 12) {
// Profile Image with Edit Button
ZStack(alignment: .bottomTrailing) {
if let image = profileImage {
Image(uiImage: image)
.resizable()
.scaledToFill()
.frame(width: Screen.height(82), height: Screen.height(82))
.clipShape(Circle())
} else {
Image(uiImage: UIImage.imgProfile)
.resizable()
.scaledToFill()
.frame(width: Screen.height(82), height: Screen.height(82))
.clipShape(Circle())
}

Button(action: {
viewModelWrapper.viewModel.editButtonTapped.accept(())
}) {
Image(uiImage: UIImage.imgEdit)
.resizable()
.frame(width: Screen.width(24), height: Screen.width(24))
.background(Color.white)
.clipShape(Circle())
}
}

// Name Label
Text(getUserName())
.font(.pretendard(.body01))
.foregroundColor(Color(UIColor.gray8))
}
.frame(height: Screen.height(120))
.padding(.top, 24)

// Level Badge - profileStackView 아래 12pt
ZStack {
RoundedRectangle(cornerRadius: Screen.height(36) / 2)
.fill(Color(UIColor.maincolor))
.frame(height: Screen.height(36))

HStack(spacing: 4) {
Text("Lv. \(viewModelWrapper.userInfo?.level ?? 1)")
.font(.pretendard(.body05))
.foregroundColor(Color(UIColor.lightGreen))

Text(getLevelText())
.font(.pretendard(.body05))
.foregroundColor(.white)
}
}
.frame(height: Screen.height(36))
.padding(.horizontal, 83)
.padding(.top, 12)

// Separator - levelView 아래 35pt
Rectangle()
.fill(Color(UIColor.green2))
.frame(height: Screen.height(6))
.padding(.top, 35)

Spacer(minLength: 25)
}
}
.onAppear {
if let urlString = viewModelWrapper.userInfo?.profileImageURL,
let url = URL(string: urlString) {
loadImage(from: url)
}
}
.onChange(of: viewModelWrapper.userInfo) { newValue in
if let urlString = newValue?.profileImageURL,
let url = URL(string: urlString) {
loadImage(from: url)
}
}
}

private func getUserName() -> String {
if let name = viewModelWrapper.userInfo?.name {
return "\(name) 님"
}
return "꾸물리안 님"
}

private func getLevelText() -> String {
viewModelWrapper.viewModel.getLevelText(for: viewModelWrapper.userInfo?.level ?? 1)
}

private func loadImage(from url: URL) {
URLSession.shared.dataTask(with: url) { data, _, _ in
if let data = data, let image = UIImage(data: data) {
DispatchQueue.main.async {
self.profileImage = image
}
}
}.resume()
}
}

// Wrapper class to bridge RxSwift and SwiftUI
class MyPageViewModelWrapper: ObservableObject {
@Published var userInfo: LoginUserModel?
let viewModel: MyPageViewModel
private let disposeBag = DisposeBag()

init(viewModel: MyPageViewModel) {
self.viewModel = viewModel

// Subscribe to RxSwift BehaviorRelay and update @Published property
viewModel.userInfo
.subscribe(onNext: { [weak self] info in
DispatchQueue.main.async {
self?.userInfo = info
}
})
.disposed(by: disposeBag)
}
}
107 changes: 107 additions & 0 deletions KkuMulKum/Source/MyPage/View/MyPageEtcSettingSwiftUIView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
//
// MyPageEtcSettingSwiftUIView.swift
// KkuMulKum
//
// Created by SwiftUI Migration on 2025/01/08.
//

import SwiftUI
import RxSwift

struct MyPageEtcSettingSwiftUIView: View {
let viewModel: MyPageViewModel
@State private var showLogoutAlert = false
@State private var showUnsubscribeAlert = false

var body: some View {
GeometryReader { geometry in
VStack(spacing: 12) {
// 버전정보
SettingRow(
title: "버전정보",
subtitle: "1.0.2",
action: {
print("버전정보 탭됨")
}
)
.frame(height: (geometry.size.height - 44 - 48) / 5) // padding 44, spacing 48
Comment on lines +20 to +27
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

반복적인 SettingRow를 생성하는 코드들을 줄일 수 있는 방법이 있을까요?


// 이용약관
SettingRow(
title: "이용약관",
action: {
NotificationCenter.default.post(
name: Notification.Name("ShowTerms"),
object: nil
)
}
)
.frame(height: (geometry.size.height - 44 - 48) / 5)

// 문의하기
SettingRow(
title: "문의하기",
action: {
NotificationCenter.default.post(
name: Notification.Name("ShowAsk"),
object: nil
)
}
)
.frame(height: (geometry.size.height - 44 - 48) / 5)

// 로그아웃
SettingRow(
title: "로그아웃",
action: {
viewModel.logoutButtonTapped.accept(())
}
)
.frame(height: (geometry.size.height - 44 - 48) / 5)

// 탈퇴하기
SettingRow(
title: "탈퇴하기",
action: {
viewModel.unsubscribeButtonTapped.accept(())
}
)
.frame(height: (geometry.size.height - 44 - 48) / 5)
}
.padding(22)
.background(Color.white)
.overlay(
RoundedRectangle(cornerRadius: 8)
.stroke(Color(UIColor.gray2), lineWidth: 1)
)
.cornerRadius(8)
}
}
}

struct SettingRow: View {
let title: String
var subtitle: String? = nil
let action: () -> Void

var body: some View {
Button(action: action) {
HStack(alignment: .center) {
Text(title)
.font(.pretendard(.body03))
.foregroundColor(Color(UIColor.gray7))

Spacer()

if let subtitle = subtitle {
Text(subtitle)
.font(.pretendard(.body03))
.foregroundColor(Color(UIColor.gray8))
}
}
.frame(maxHeight: .infinity)
.contentShape(Rectangle())
}
.buttonStyle(PlainButtonStyle())
}
}
41 changes: 41 additions & 0 deletions KkuMulKum/Source/MyPage/View/MyPageSwiftUIView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// MyPageSwiftUIView.swift
// KkuMulKum
//
// Created by SwiftUI Migration on 2025/01/08.
//

import SwiftUI
import RxSwift

struct MyPageSwiftUIView: View {
@StateObject private var viewModelWrapper: MyPageViewModelWrapper

init(viewModel: MyPageViewModel) {
_viewModelWrapper = StateObject(wrappedValue: MyPageViewModelWrapper(viewModel: viewModel))
}

var body: some View {
GeometryReader { geometry in
ZStack {
Color(UIColor.green1)
.ignoresSafeArea()

VStack(spacing: 0) {
MyPageContentSwiftUIView(viewModel: viewModelWrapper.viewModel)
.frame(height: 24 + Screen.height(120) + 12 + Screen.height(36) + 35 + Screen.height(6) + 25)

let contentHeight = 24 + Screen.height(120) + 12 + Screen.height(36) + 35 + Screen.height(6) + 25
let etcHeight = geometry.size.height - contentHeight - 12 - 127

MyPageEtcSettingSwiftUIView(viewModel: viewModelWrapper.viewModel)
.frame(height: max(0, etcHeight))
.padding(.horizontal, 20)
.padding(.top, 12)

Spacer(minLength: 127)
}
}
}
}
}
Loading