feat: 新增应用设置功能及相关视图
- 在MainFeature中集成AppSettingFeature,支持应用设置页面的导航与状态管理。 - 新增AppSettingView以展示用户头像和昵称编辑功能,提升用户体验。 - 更新MainView以支持应用设置页面的展示,增强导航功能。
This commit is contained in:
27
yana/Features/AppSettingFeature.swift
Normal file
27
yana/Features/AppSettingFeature.swift
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import Foundation
|
||||||
|
import ComposableArchitecture
|
||||||
|
|
||||||
|
struct AppSettingFeature: Reducer {
|
||||||
|
struct State: Equatable {
|
||||||
|
var nickname: String = "hahahaha"
|
||||||
|
var avatarURL: String? = nil
|
||||||
|
}
|
||||||
|
enum Action: Equatable {
|
||||||
|
case onAppear
|
||||||
|
case editNicknameTapped
|
||||||
|
case logoutTapped
|
||||||
|
// 可扩展更多 action
|
||||||
|
}
|
||||||
|
func reduce(into state: inout State, action: Action) -> Effect<Action> {
|
||||||
|
switch action {
|
||||||
|
case .onAppear:
|
||||||
|
return .none
|
||||||
|
case .editNicknameTapped:
|
||||||
|
// 预留编辑昵称逻辑
|
||||||
|
return .none
|
||||||
|
case .logoutTapped:
|
||||||
|
// 预留登出逻辑
|
||||||
|
return .none
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -15,12 +15,14 @@ struct MainFeature: Reducer {
|
|||||||
// 新增:导航路径和设置页面 State
|
// 新增:导航路径和设置页面 State
|
||||||
var navigationPath: [Destination] = []
|
var navigationPath: [Destination] = []
|
||||||
var settingState: SettingFeature.State? = nil
|
var settingState: SettingFeature.State? = nil
|
||||||
|
var appSettingState: AppSettingFeature.State? = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增:导航目标
|
// 新增:导航目标
|
||||||
enum Destination: Hashable, Equatable {
|
enum Destination: Hashable, Equatable {
|
||||||
case setting
|
case setting
|
||||||
case test
|
case test
|
||||||
|
case appSetting
|
||||||
}
|
}
|
||||||
|
|
||||||
@CasePathable
|
@CasePathable
|
||||||
@@ -35,6 +37,8 @@ struct MainFeature: Reducer {
|
|||||||
case settingButtonTapped
|
case settingButtonTapped
|
||||||
case settingAction(SettingFeature.Action)
|
case settingAction(SettingFeature.Action)
|
||||||
case testButtonTapped
|
case testButtonTapped
|
||||||
|
case appSettingButtonTapped
|
||||||
|
case appSettingAction(AppSettingFeature.Action)
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some ReducerOf<Self> {
|
var body: some ReducerOf<Self> {
|
||||||
@@ -66,8 +70,8 @@ struct MainFeature: Reducer {
|
|||||||
return .none
|
return .none
|
||||||
case .me(.settingButtonTapped):
|
case .me(.settingButtonTapped):
|
||||||
// 触发 push 到设置页
|
// 触发 push 到设置页
|
||||||
state.settingState = SettingFeature.State()
|
state.appSettingState = AppSettingFeature.State()
|
||||||
state.navigationPath.append(.setting)
|
state.navigationPath.append(.appSetting)
|
||||||
return .none
|
return .none
|
||||||
case .me:
|
case .me:
|
||||||
return .none
|
return .none
|
||||||
@@ -76,6 +80,9 @@ struct MainFeature: Reducer {
|
|||||||
if !newPath.contains(.setting) {
|
if !newPath.contains(.setting) {
|
||||||
state.settingState = nil
|
state.settingState = nil
|
||||||
}
|
}
|
||||||
|
if !newPath.contains(.appSetting) {
|
||||||
|
state.appSettingState = nil
|
||||||
|
}
|
||||||
state.navigationPath = newPath
|
state.navigationPath = newPath
|
||||||
return .none
|
return .none
|
||||||
case .settingButtonTapped:
|
case .settingButtonTapped:
|
||||||
@@ -87,11 +94,20 @@ struct MainFeature: Reducer {
|
|||||||
case .testButtonTapped:
|
case .testButtonTapped:
|
||||||
state.navigationPath.append(.test)
|
state.navigationPath.append(.test)
|
||||||
return .none
|
return .none
|
||||||
|
case .appSettingButtonTapped:
|
||||||
|
state.appSettingState = AppSettingFeature.State()
|
||||||
|
state.navigationPath.append(.appSetting)
|
||||||
|
return .none
|
||||||
|
case .appSettingAction:
|
||||||
|
return .none
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 设置页作用域
|
// 设置页作用域
|
||||||
.ifLet(\ .settingState, action: /Action.settingAction) {
|
.ifLet(\ .settingState, action: /Action.settingAction) {
|
||||||
SettingFeature()
|
SettingFeature()
|
||||||
}
|
}
|
||||||
|
.ifLet(\ .appSettingState, action: /Action.appSettingAction) {
|
||||||
|
AppSettingFeature()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
98
yana/Views/AppSettingView.swift
Normal file
98
yana/Views/AppSettingView.swift
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
import SwiftUI
|
||||||
|
import ComposableArchitecture
|
||||||
|
|
||||||
|
struct AppSettingView: View {
|
||||||
|
let store: StoreOf<AppSettingFeature>
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
WithViewStore(self.store, observe: { $0 }) { viewStore in
|
||||||
|
ZStack {
|
||||||
|
Color(red: 22/255, green: 17/255, blue: 44/255).ignoresSafeArea()
|
||||||
|
VStack(spacing: 0) {
|
||||||
|
Spacer().frame(height: 24)
|
||||||
|
// 顶部标题
|
||||||
|
Text("Edit")
|
||||||
|
.font(.system(size: 22, weight: .semibold))
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.padding(.top, 8)
|
||||||
|
// 头像
|
||||||
|
ZStack(alignment: .bottomTrailing) {
|
||||||
|
Image("avatar_placeholder")
|
||||||
|
.resizable()
|
||||||
|
.aspectRatio(contentMode: .fill)
|
||||||
|
.frame(width: 120, height: 120)
|
||||||
|
.clipShape(Circle())
|
||||||
|
Button(action: {}) {
|
||||||
|
ZStack {
|
||||||
|
Circle().fill(Color.purple).frame(width: 36, height: 36)
|
||||||
|
Image(systemName: "camera.fill")
|
||||||
|
.foregroundColor(.white)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.offset(x: 8, y: 8)
|
||||||
|
}
|
||||||
|
.padding(.top, 24)
|
||||||
|
// 昵称
|
||||||
|
HStack {
|
||||||
|
Text("Nickname")
|
||||||
|
.foregroundColor(.white)
|
||||||
|
Spacer()
|
||||||
|
Text(viewStore.nickname)
|
||||||
|
.foregroundColor(.gray)
|
||||||
|
Image(systemName: "chevron.right")
|
||||||
|
.foregroundColor(.gray)
|
||||||
|
}
|
||||||
|
.padding(.horizontal, 32)
|
||||||
|
.padding(.vertical, 18)
|
||||||
|
.onTapGesture {
|
||||||
|
viewStore.send(.editNicknameTapped)
|
||||||
|
}
|
||||||
|
Divider().background(Color.gray.opacity(0.3))
|
||||||
|
.padding(.horizontal, 32)
|
||||||
|
// 其他设置项
|
||||||
|
VStack(spacing: 0) {
|
||||||
|
settingRow("Personal Information and Permissions")
|
||||||
|
settingRow("Help")
|
||||||
|
settingRow("Clear Cache")
|
||||||
|
settingRow("Check for Updates")
|
||||||
|
settingRow("Log Out")
|
||||||
|
settingRow("About Us")
|
||||||
|
}
|
||||||
|
.background(Color.clear)
|
||||||
|
.padding(.horizontal, 0)
|
||||||
|
Spacer()
|
||||||
|
// 底部大按钮
|
||||||
|
Button(action: {
|
||||||
|
viewStore.send(.logoutTapped)
|
||||||
|
}) {
|
||||||
|
Text("Log out of account")
|
||||||
|
.font(.system(size: 18, weight: .semibold))
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
.padding(.vertical, 18)
|
||||||
|
.background(Color.white.opacity(0.08))
|
||||||
|
.cornerRadius(28)
|
||||||
|
.padding(.horizontal, 32)
|
||||||
|
}
|
||||||
|
.padding(.bottom, 32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 设置项行
|
||||||
|
func settingRow(_ title: String) -> some View {
|
||||||
|
return VStack(spacing: 0) {
|
||||||
|
HStack {
|
||||||
|
Text(title)
|
||||||
|
.foregroundColor(.white)
|
||||||
|
Spacer()
|
||||||
|
Image(systemName: "chevron.right")
|
||||||
|
.foregroundColor(.gray)
|
||||||
|
}
|
||||||
|
.padding(.horizontal, 32)
|
||||||
|
.padding(.vertical, 18)
|
||||||
|
Divider().background(Color.gray.opacity(0.3))
|
||||||
|
.padding(.horizontal, 32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -78,6 +78,18 @@ struct MainView: View {
|
|||||||
)
|
)
|
||||||
case .test:
|
case .test:
|
||||||
TestPushView()
|
TestPushView()
|
||||||
|
case .appSetting:
|
||||||
|
IfLetStore(
|
||||||
|
self.store.scope(
|
||||||
|
state: \.appSettingState,
|
||||||
|
action: MainFeature.Action.appSettingAction
|
||||||
|
),
|
||||||
|
then: { appSettingStore in
|
||||||
|
WithPerceptionTracking {
|
||||||
|
AppSettingView(store: appSettingStore)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user