fix View
This commit is contained in:
parent
97194e501e
commit
067ac7c092
@ -77,8 +77,6 @@ struct LoginView: View {
|
|||||||
.padding(.bottom, 20)
|
.padding(.bottom, 20)
|
||||||
}
|
}
|
||||||
.frame(width: 380, height: 520)
|
.frame(width: 380, height: 520)
|
||||||
// 关键:macOS 标准毛玻璃背景
|
|
||||||
.background(VisualEffectView(material: .hudWindow, blendingMode: .behindWindow))
|
|
||||||
.ignoresSafeArea()
|
.ignoresSafeArea()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,25 +286,6 @@ struct CustomSecureField: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - 1. 基础 UI 组件 (已修正 Material 枚举)
|
|
||||||
struct VisualEffectView: NSViewRepresentable {
|
|
||||||
let material: NSVisualEffectView.Material
|
|
||||||
let blendingMode: NSVisualEffectView.BlendingMode
|
|
||||||
|
|
||||||
func makeNSView(context: Context) -> NSVisualEffectView {
|
|
||||||
let view = NSVisualEffectView()
|
|
||||||
view.material = material
|
|
||||||
view.blendingMode = blendingMode
|
|
||||||
view.state = .active
|
|
||||||
return view
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateNSView(_ nsView: NSVisualEffectView, context: Context) {
|
|
||||||
nsView.material = material
|
|
||||||
nsView.blendingMode = blendingMode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#Preview {
|
#Preview {
|
||||||
LoginView()
|
LoginView()
|
||||||
.environment(AppContext(noticePort: 0))
|
.environment(AppContext(noticePort: 0))
|
||||||
|
|||||||
@ -14,11 +14,10 @@ struct RegisterRootView: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
// 背景毛玻璃
|
Color.clear
|
||||||
VisualEffectView(material: .underWindowBackground, blendingMode: .behindWindow)
|
|
||||||
.ignoresSafeArea()
|
.ignoresSafeArea()
|
||||||
|
|
||||||
Group {
|
ZStack(alignment: .center) {
|
||||||
switch registerModel.stage {
|
switch registerModel.stage {
|
||||||
case .requestVerifyCode:
|
case .requestVerifyCode:
|
||||||
RegisterRequestVerifyCodeView()
|
RegisterRequestVerifyCodeView()
|
||||||
@ -64,7 +63,6 @@ struct RegisterRootView: View {
|
|||||||
.transition(.opacity) // 按钮出现的动画
|
.transition(.opacity) // 按钮出现的动画
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.frame(width: 500, height: 400)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,7 +120,9 @@ struct RegisterRequestVerifyCodeView: View {
|
|||||||
.frame(width: 280)
|
.frame(width: 280)
|
||||||
|
|
||||||
Button(action: {
|
Button(action: {
|
||||||
self.requestVerifyCode(username: model.username)
|
Task { @MainActor in
|
||||||
|
await self.requestVerifyCode(username: model.username)
|
||||||
|
}
|
||||||
}) {
|
}) {
|
||||||
Text("获取验证码")
|
Text("获取验证码")
|
||||||
.fontWeight(.medium)
|
.fontWeight(.medium)
|
||||||
@ -141,13 +141,15 @@ struct RegisterRequestVerifyCodeView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func requestVerifyCode(username: String) {
|
private func requestVerifyCode(username: String) async {
|
||||||
self.isProcessing = true
|
self.isProcessing = true
|
||||||
Task { @MainActor in
|
defer {
|
||||||
|
self.isProcessing = false
|
||||||
|
}
|
||||||
|
|
||||||
if username.isEmpty {
|
if username.isEmpty {
|
||||||
self.showAlert = true
|
self.showAlert = true
|
||||||
self.errorMessage = "邮箱为空"
|
self.errorMessage = "邮箱为空"
|
||||||
self.isProcessing = false
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,8 +175,6 @@ struct RegisterRequestVerifyCodeView: View {
|
|||||||
self.showAlert = true
|
self.showAlert = true
|
||||||
self.errorMessage = "邮箱格式错误"
|
self.errorMessage = "邮箱格式错误"
|
||||||
}
|
}
|
||||||
self.isProcessing = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,11 +14,10 @@ struct ResetPasswordRootView: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
// 背景毛玻璃 (macOS 风格)
|
Color.clear
|
||||||
VisualEffectView(material: .underWindowBackground, blendingMode: .behindWindow)
|
|
||||||
.ignoresSafeArea()
|
.ignoresSafeArea()
|
||||||
|
|
||||||
Group {
|
ZStack(alignment: .center) {
|
||||||
switch resetPasswordModel.stage {
|
switch resetPasswordModel.stage {
|
||||||
case .requestVerifyCode:
|
case .requestVerifyCode:
|
||||||
GetVerifyCodeView()
|
GetVerifyCodeView()
|
||||||
@ -60,7 +59,6 @@ struct ResetPasswordRootView: View {
|
|||||||
.transition(.opacity) // 按钮出现的动画
|
.transition(.opacity) // 按钮出现的动画
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.frame(width: 500, height: 400)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,32 +9,59 @@ import SwiftUI
|
|||||||
|
|
||||||
struct RootView: View {
|
struct RootView: 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
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
// 主要界面
|
// 1. 主要界面容器
|
||||||
Group {
|
// 使用 ZStack 代替 Group,因为它在处理内容对齐和转场重叠时更稳定
|
||||||
|
ZStack(alignment: .center) {
|
||||||
switch appContext.appScene {
|
switch appContext.appScene {
|
||||||
case .login(username: let username):
|
case .login(username: let username):
|
||||||
LoginView(username: username)
|
LoginView(username: username)
|
||||||
|
.id("scene_login") // 显式 ID 确保转场触发
|
||||||
case .logined:
|
case .logined:
|
||||||
NetworkView()
|
NetworkView()
|
||||||
|
.id("scene_logined")
|
||||||
case .register:
|
case .register:
|
||||||
RegisterRootView()
|
RegisterRootView()
|
||||||
|
.id("scene_register")
|
||||||
case .resetPassword:
|
case .resetPassword:
|
||||||
ResetPasswordRootView()
|
ResetPasswordRootView()
|
||||||
|
.id("scene_reset")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 2. 关键:设置容器尺寸行为
|
||||||
|
// 如果是全屏应用,用 maxWidth/Height .infinity
|
||||||
|
// 如果是固定大小窗口,可以在这里写死 minWidth/minHeight
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
// 3. 限制裁剪,防止位移动画超出 RootView 的边界
|
||||||
|
.clipped()
|
||||||
.transition(.asymmetric(
|
.transition(.asymmetric(
|
||||||
insertion: .move(edge: .trailing).combined(with: .opacity),
|
insertion: .move(edge: .trailing).combined(with: .opacity),
|
||||||
removal: .move(edge: .trailing).combined(with: .opacity)
|
removal: .move(edge: .leading).combined(with: .opacity) // 修改为 leading 更有“流转”感
|
||||||
))
|
))
|
||||||
|
|
||||||
// 自动更新遮罩
|
// 自动更新遮罩层级
|
||||||
if updateManager.showUpdateOverlay, let info = updateManager.updateInfo {
|
if updateManager.showUpdateOverlay, let info = updateManager.updateInfo {
|
||||||
// 遮罩背景
|
updateOverlay(info: info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 4. 统一处理 Scene 切换的动画
|
||||||
|
.animation(.spring(duration: 0.5), value: appContext.appScene)
|
||||||
|
.animation(.spring(duration: 0.4), value: updateManager.showUpdateOverlay)
|
||||||
|
// 关键:macOS 标准毛玻璃背景
|
||||||
|
.background(VisualEffectView(material: .hudWindow, blendingMode: .behindWindow))
|
||||||
|
.task {
|
||||||
|
let checkUpdateResult = await updateManager.checkUpdate(isManual: false)
|
||||||
|
NSLog("[RootView] checkUpdateResult: \(checkUpdateResult)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将遮罩抽离,保持 body 清爽
|
||||||
|
@ViewBuilder
|
||||||
|
private func updateOverlay(info: SDLAPIClient.AppUpgradeInfo) -> some View {
|
||||||
|
ZStack {
|
||||||
Color.black.opacity(0.4)
|
Color.black.opacity(0.4)
|
||||||
.ignoresSafeArea()
|
.ignoresSafeArea()
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
@ -43,27 +70,19 @@ struct RootView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 弹窗卡片
|
|
||||||
AppUpdateView(info: info) {
|
AppUpdateView(info: info) {
|
||||||
updateManager.showUpdateOverlay = false
|
updateManager.showUpdateOverlay = false
|
||||||
}
|
}
|
||||||
.clipShape(RoundedRectangle(cornerRadius: 16))
|
.clipShape(RoundedRectangle(cornerRadius: 16))
|
||||||
.shadow(color: .black.opacity(0.3), radius: 20)
|
.shadow(color: .black.opacity(0.3), radius: 20)
|
||||||
|
}
|
||||||
.transition(.asymmetric(
|
.transition(.asymmetric(
|
||||||
insertion: .scale(scale: 0.9).combined(with: .opacity),
|
insertion: .scale(scale: 0.9).combined(with: .opacity),
|
||||||
removal: .opacity
|
removal: .opacity
|
||||||
))
|
))
|
||||||
|
.zIndex(100) // 确保更新遮罩永远在最上层
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.animation(.spring(duration: 0.4), value: updateManager.showUpdateOverlay)
|
|
||||||
.task {
|
|
||||||
// 启动时静默检查
|
|
||||||
let checkUpdateResult = await updateManager.checkUpdate(isManual: false)
|
|
||||||
NSLog("[RootView] checkUpdateResult: \(checkUpdateResult)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#Preview {
|
#Preview {
|
||||||
RootView()
|
RootView()
|
||||||
|
|||||||
@ -55,16 +55,7 @@ struct punchnetApp: App {
|
|||||||
//.interactiveDismissDisabled() // 强制阅读
|
//.interactiveDismissDisabled() // 强制阅读
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// .commands {
|
.windowResizability(.contentSize)
|
||||||
// CommandGroup(replacing: .appInfo) {
|
|
||||||
// Button {
|
|
||||||
// openWindow(id: "abortPunchnet")
|
|
||||||
// } label: {
|
|
||||||
// Text("About Punchnet")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//.windowResizability(.contentSize)
|
|
||||||
.windowToolbarStyle(.unified)
|
.windowToolbarStyle(.unified)
|
||||||
.defaultPosition(.center)
|
.defaultPosition(.center)
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user