解决UI的布局问题

This commit is contained in:
anlicheng 2026-03-24 18:05:42 +08:00
parent 21be7cef58
commit e4a0728345
6 changed files with 65 additions and 73 deletions

View File

@ -16,7 +16,14 @@ class AppContext {
var networkContext: SDLAPIClient.NetworkContext = .default() var networkContext: SDLAPIClient.NetworkContext = .default()
// app // app
var appScene: AppScene = .login(username: nil) var loginScene: LoginScene = .login(username: nil)
//
enum LoginScene: Equatable {
case login(username: String?)
case register
case resetPassword
}
// //
var loginCredit: Credit? var loginCredit: Credit?
@ -40,14 +47,6 @@ class AppContext {
} }
} }
//
enum AppScene: Equatable {
case login(username: String?)
case logined
case register
case resetPassword
}
init(noticePort: Int) { init(noticePort: Int) {
self.noticePort = noticePort self.noticePort = noticePort
} }

View File

@ -77,7 +77,6 @@ struct LoginView: View {
} }
.padding(.bottom, 20) .padding(.bottom, 20)
} }
.frame(width: 380, height: 520)
} }
} }
@ -106,7 +105,7 @@ struct LoginAccountView: View {
HStack { HStack {
Button("注册") { Button("注册") {
withAnimation(.spring(duration: 0.6, bounce: 0.2)) { withAnimation(.spring(duration: 0.6, bounce: 0.2)) {
self.appContext.appScene = .register self.appContext.loginScene = .register
} }
} }
.buttonStyle(.link) .buttonStyle(.link)
@ -115,7 +114,7 @@ struct LoginAccountView: View {
Button("忘记密码?") { Button("忘记密码?") {
withAnimation(.spring(duration: 0.6, bounce: 0.2)) { withAnimation(.spring(duration: 0.6, bounce: 0.2)) {
self.appContext.appScene = .resetPassword self.appContext.loginScene = .resetPassword
} }
} }
.buttonStyle(.link) .buttonStyle(.link)
@ -170,7 +169,7 @@ struct LoginAccountView: View {
do { do {
_ = try await appContext.loginWith(username: username, password: password) _ = try await appContext.loginWith(username: username, password: password)
withAnimation(.spring(duration: 0.6, bounce: 0.2)) { withAnimation(.spring(duration: 0.6, bounce: 0.2)) {
self.appContext.appScene = .logined //self.appContext.appScene = .logined
} }
} catch let err as SDLAPIError { } catch let err as SDLAPIError {
self.showAlert = true self.showAlert = true
@ -231,7 +230,7 @@ struct LoginTokenView: View {
do { do {
_ = try await appContext.loginWith(token: token) _ = try await appContext.loginWith(token: token)
withAnimation(.spring(duration: 0.6, bounce: 0.2)) { withAnimation(.spring(duration: 0.6, bounce: 0.2)) {
self.appContext.appScene = .logined //self.appContext.appScene = .logined
} }
} catch let err as SDLAPIError { } catch let err as SDLAPIError {
self.showAlert = true self.showAlert = true

View File

@ -7,7 +7,7 @@
import SwiftUI import SwiftUI
struct RootView: View { struct LoginRootView: View {
@Environment(AppContext.self) var appContext: AppContext @Environment(AppContext.self) var appContext: AppContext
@State private var updateManager = AppUpdateManager.shared @State private var updateManager = AppUpdateManager.shared
@ -16,13 +16,10 @@ struct RootView: View {
// 1. // 1.
// 使 ZStack Group // 使 ZStack Group
ZStack(alignment: .center) { ZStack(alignment: .center) {
switch appContext.appScene { switch appContext.loginScene {
case .login(username: let username): case .login(username: let username):
LoginView(username: username) LoginView(username: username)
.id("scene_login") // ID .id("scene_login") // ID
case .logined:
NetworkView()
.id("scene_logined")
case .register: case .register:
RegisterRootView() RegisterRootView()
.id("scene_register") .id("scene_register")
@ -32,9 +29,7 @@ struct RootView: View {
} }
} }
// 2. // 2.
// maxWidth/Height .infinity .frame(width: 380, height: 500)
// minWidth/minHeight
.frame(maxWidth: .infinity, maxHeight: .infinity)
// 3. RootView // 3. RootView
.clipped() .clipped()
.transition(.asymmetric( .transition(.asymmetric(
@ -48,7 +43,7 @@ struct RootView: View {
} }
} }
// 4. Scene // 4. Scene
.animation(.spring(duration: 0.5), value: appContext.appScene) .animation(.spring(duration: 0.5), value: appContext.loginScene)
.animation(.spring(duration: 0.4), value: updateManager.showUpdateOverlay) .animation(.spring(duration: 0.4), value: updateManager.showUpdateOverlay)
// macOS // macOS
.background(VisualEffectView(material: .hudWindow, blendingMode: .behindWindow)) .background(VisualEffectView(material: .hudWindow, blendingMode: .behindWindow))
@ -82,8 +77,5 @@ struct RootView: View {
)) ))
.zIndex(100) // .zIndex(100) //
} }
}
#Preview {
RootView()
} }

View File

@ -14,21 +14,25 @@ struct RegisterRootView: View {
var body: some View { var body: some View {
ZStack(alignment: .center) { ZStack(alignment: .center) {
switch registerModel.stage { Color.clear
case .requestVerifyCode:
RegisterRequestVerifyCodeView() ZStack(alignment: .center) {
case .submitVerifyCode: switch registerModel.stage {
RegisterSubmitVerifyCodeView() case .requestVerifyCode:
case .setPassword: RegisterRequestVerifyCodeView()
RegisterSetPasswordView() case .submitVerifyCode:
case .success: RegisterSubmitVerifyCodeView()
RegisterSuccessView() case .setPassword:
RegisterSetPasswordView()
case .success:
RegisterSuccessView()
}
} }
.transition(.asymmetric(
insertion: .move(edge: registerModel.transitionEdge).combined(with: .opacity),
removal: .move(edge: registerModel.transitionEdge == .trailing ? .leading : .trailing).combined(with: .opacity)
))
} }
.transition(.asymmetric(
insertion: .move(edge: registerModel.transitionEdge).combined(with: .opacity),
removal: .move(edge: registerModel.transitionEdge == .trailing ? .leading : .trailing).combined(with: .opacity)
))
.environment(registerModel) .environment(registerModel)
// --- 使 overlay --- // --- 使 overlay ---
.overlay(alignment: .topLeading) { .overlay(alignment: .topLeading) {
@ -40,7 +44,7 @@ struct RegisterRootView: View {
Button(action: { Button(action: {
// //
withAnimation(.spring(duration: 0.6, bounce: 0.2)) { withAnimation(.spring(duration: 0.6, bounce: 0.2)) {
self.appContext.appScene = .login(username: nil) self.appContext.loginScene = .login(username: nil)
} }
}) { }) {
HStack { HStack {
@ -127,8 +131,6 @@ struct RegisterRequestVerifyCodeView: View {
.controlSize(.large) .controlSize(.large)
.frame(width: 280) .frame(width: 280)
.disabled(!SDLUtil.isValidIdentifyContact(model.username) || isProcessing) .disabled(!SDLUtil.isValidIdentifyContact(model.username) || isProcessing)
Spacer()
} }
.padding(40) .padding(40)
.alert(isPresented: $showAlert) { .alert(isPresented: $showAlert) {
@ -244,8 +246,6 @@ struct RegisterSubmitVerifyCodeView: View {
.foregroundColor(.secondary) .foregroundColor(.secondary)
} }
.frame(width: 280) .frame(width: 280)
Spacer()
} }
.padding(40) .padding(40)
.alert(isPresented: $showAlert) { .alert(isPresented: $showAlert) {
@ -362,8 +362,6 @@ struct RegisterSetPasswordView: View {
.controlSize(.large) .controlSize(.large)
.frame(width: 280) .frame(width: 280)
.disabled(passwordError != nil) .disabled(passwordError != nil)
Spacer()
} }
.padding(40) .padding(40)
.alert(isPresented: $showAlert) { .alert(isPresented: $showAlert) {
@ -441,7 +439,7 @@ struct RegisterSuccessView: View {
Button(action: { Button(action: {
// //
withAnimation(.spring(duration: 0.6, bounce: 0.2)) { withAnimation(.spring(duration: 0.6, bounce: 0.2)) {
self.appContext.appScene = .login(username: registerModel.username) self.appContext.loginScene = .login(username: registerModel.username)
} }
}) { }) {
Text("立即开始使用") Text("立即开始使用")

View File

@ -14,21 +14,25 @@ struct ResetPasswordRootView: View {
var body: some View { var body: some View {
ZStack(alignment: .center) { ZStack(alignment: .center) {
switch resetPasswordModel.stage { Color.clear
case .requestVerifyCode:
GetVerifyCodeView() ZStack(alignment: .center) {
case .submitVerifyCode: switch resetPasswordModel.stage {
SubmitVerifyCodeView() case .requestVerifyCode:
case .resetPassword: GetVerifyCodeView()
ResetPasswordView() case .submitVerifyCode:
case .success: SubmitVerifyCodeView()
ResetPasswordSuccessView() case .resetPassword:
ResetPasswordView()
case .success:
ResetPasswordSuccessView()
}
} }
.transition(.asymmetric(
insertion: .move(edge: resetPasswordModel.transitionEdge).combined(with: .opacity),
removal: .move(edge: resetPasswordModel.transitionEdge == .trailing ? .leading : .trailing).combined(with: .opacity)
))
} }
.transition(.asymmetric(
insertion: .move(edge: resetPasswordModel.transitionEdge).combined(with: .opacity),
removal: .move(edge: resetPasswordModel.transitionEdge == .trailing ? .leading : .trailing).combined(with: .opacity)
))
.environment(resetPasswordModel) .environment(resetPasswordModel)
.overlay(alignment: .topLeading) { .overlay(alignment: .topLeading) {
// //
@ -36,7 +40,7 @@ struct ResetPasswordRootView: View {
Button(action: { Button(action: {
// //
withAnimation(.spring(duration: 0.6, bounce: 0.2)) { withAnimation(.spring(duration: 0.6, bounce: 0.2)) {
self.appContext.appScene = .login(username: nil) self.appContext.loginScene = .login(username: nil)
} }
}) { }) {
HStack { HStack {
@ -86,8 +90,6 @@ struct GetVerifyCodeView: View {
.controlSize(.large) .controlSize(.large)
.frame(width: 280) .frame(width: 280)
.disabled(!SDLUtil.isValidIdentifyContact(model.username) || isProcessing) .disabled(!SDLUtil.isValidIdentifyContact(model.username) || isProcessing)
Spacer()
} }
.padding(40) .padding(40)
.alert(isPresented: $showAlert) { .alert(isPresented: $showAlert) {
@ -179,8 +181,6 @@ struct SubmitVerifyCodeView: View {
.foregroundColor(.secondary) .foregroundColor(.secondary)
} }
.frame(width: 280) .frame(width: 280)
Spacer()
} }
.padding(40) .padding(40)
.task { .task {
@ -294,8 +294,6 @@ struct ResetPasswordView: View {
.controlSize(.large) .controlSize(.large)
.frame(width: 280) .frame(width: 280)
.disabled(!isInputValid || isProcessing) .disabled(!isInputValid || isProcessing)
Spacer()
} }
.padding(40) .padding(40)
.alert(isPresented: $showAlert) { .alert(isPresented: $showAlert) {
@ -364,7 +362,7 @@ struct ResetPasswordSuccessView: View {
Button(action: { Button(action: {
withAnimation(.spring(duration: 0.6, bounce: 0.2)) { withAnimation(.spring(duration: 0.6, bounce: 0.2)) {
self.appContext.appScene = .login(username: self.resetPasswordModel.username) self.appContext.loginScene = .login(username: self.resetPasswordModel.username)
} }
}) { }) {
Text("返回登录") Text("返回登录")
@ -377,8 +375,6 @@ struct ResetPasswordSuccessView: View {
} }
.opacity(animateText ? 1.0 : 0.0) .opacity(animateText ? 1.0 : 0.0)
.offset(y: animateText ? 0 : 20) .offset(y: animateText ? 0 : 20)
Spacer()
} }
.padding(40) .padding(40)
.frame(maxWidth: .infinity, maxHeight: .infinity) .frame(maxWidth: .infinity, maxHeight: .infinity)

View File

@ -43,8 +43,8 @@ struct punchnetApp: App {
} }
var body: some Scene { var body: some Scene {
WindowGroup(id: "main") { Window("登陆", id: "login") {
RootView() LoginRootView()
.navigationTitle("") .navigationTitle("")
.environment(self.appContext) .environment(self.appContext)
.onAppear { .onAppear {
@ -59,6 +59,14 @@ struct punchnetApp: App {
.windowToolbarStyle(.unified) .windowToolbarStyle(.unified)
.defaultPosition(.center) .defaultPosition(.center)
Window("网络", id: "logined") {
SettingsView()
.environment(self.appContext)
.frame(width: 750, height: 500)
}
.windowResizability(.contentSize)
.defaultPosition(.center)
Window("设置", id: "settings") { Window("设置", id: "settings") {
SettingsView() SettingsView()
.environment(self.appContext) .environment(self.appContext)