Apple Watch上SwiftUI Sheet关闭后重复弹出问题求助
Apple Watch健身应用Sheet重复弹出问题
我正在开发一款Apple Watch健身应用,用户可在开始运动前选择强度。尝试在导航至实际运动视图前,以Sheet形式展示强度选择器,但遇到问题:关闭Sheet后它会立即重新弹出。使用环境为Xcode 14.2和watchOS 9.1。
相关代码
主视图(Sheet控制视图)
主视图通过workoutManager.showingZonePickerView控制Sheet的展示:
import SwiftUI @main struct Wise_Watch_AppApp: App { @StateObject private var workoutManager = WorkoutManager() @SceneBuilder var body: some Scene { WindowGroup { NavigationView { WorkoutView() } .sheet(isPresented: $workoutManager.showingZonePickerView, onDismiss: { workoutManager.showingZonePickerView = false }) { WorkoutLevelPickerView(total: 5, completed: 1) .toolbar(.hidden) } .sheet(isPresented: $workoutManager.showingSummaryView) { SummaryView() } .environmentObject(workoutManager) } } }
运动类型选择视图
用户选择偏好运动类型的视图,通过NavigationLink跳转到运动会话视图:
import SwiftUI import HealthKit struct WorkoutView: View { @EnvironmentObject var workoutManager: WorkoutManager @State var linkActive: Bool = false var workoutTypes: [HKWorkoutActivityType] = [.cycling, .running, .walking] var workoutDictionary: Dictionary<String, HKWorkoutActivityType> = [ "figure.outdoor.cycle" : .cycling, "figure.run" : .running, "figure.walk" : .walking ] var body: some View { List(Array(workoutDictionary.keys), id: \.self) { workoutType in NavigationLink( destination: SessionPagingView(), tag: workoutDictionary[workoutType]!, selection: $workoutManager.selectedWorkout ) { Label("\(workoutDictionary[workoutType]!.name)", systemImage: workoutType) .font(.subheadline) .fontWeight(.semibold) .padding() } .padding(EdgeInsets(top: 15, leading: 5, bottom: 15, trailing: 5)) } .listStyle(.carousel) .navigationBarTitle("Workouts") .onAppear { workoutManager.requestAuthorization() } } }
运动会话视图
点击NavigationLink后进入的视图,在onAppear中触发Sheet展示:
import SwiftUI import WatchKit import ConfettiSwiftUI struct SessionPagingView: View { @EnvironmentObject var workoutManager: WorkoutManager @State private var selection: Tab = .metrics @State private var counter: Int = 0 @State private var isViewHidden: Bool = true enum Tab { case metrics, nowPlaying, milestone } var body: some View { if(isViewHidden) { sessionView.hidden() } else { sessionView } } var sessionView: some View { TabView(selection: $selection) { ForEach(workoutManager.tabItems) { item in VStack { Text("Congrats!") .font(.title2) .fontWeight(.bold) .confettiCannon(counter: $counter, num: 40, radius: 200) Text("You just reached") .font(.subheadline) .foregroundColor(.cyan) .tag(Tab.milestone) Text("\(item.value) km") .font(.subheadline) .foregroundColor(.cyan) .tag(Tab.milestone) } } MetricsView().tag(Tab.metrics) NowPlayingView().tag(Tab.nowPlaying) } .navigationBarHidden(true) .navigationBarBackButtonHidden(true) .onAppear { workoutManager.showingZonePickerView = true DispatchQueue.main.asyncAfter(deadline: .now() + 1) { self.isViewHidden = false } } .onChange(of: workoutManager.running) { _ in displayMetricsView() } .onChange(of: workoutManager.tabItems) { _ in if(workoutManager.tabItems.count > 0) { displayMilestoneView() DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(5), execute: { displayMetricsView() }) } } .ignoresSafeArea() .animation(.easeInOut, value: self.selection) } private func displayMetricsView() { withAnimation { selection = .metrics } } private func displayMilestoneView() { withAnimation { selection = .milestone WKInterfaceDevice.current().play(.notification) DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1), execute: { counter += 1 }) } } }
强度选择Sheet视图
Sheet中展示的强度选择器视图,通过dismiss()关闭Sheet:
import SwiftUI struct WorkoutLevelPickerView: View { @EnvironmentObject var workoutManager: WorkoutManager @Environment(\.dismiss) private var dismiss let total: Int @State var lineWidth: CGFloat = 16 @State var color: Color = .green @State var completed: Double = 1.0 @State var currentZone: HeartRateZone = zones[0] @State var isScrolling: Bool = false var body: some View { VStack { ZStack { CircleLabelView( radius: 30, tracking: 0, size: .init(width: 120, height: 120), text: currentZone.intensity.uppercased() ) .font(.headline) .frame(width: 50, height: 50) .rotationEffect(Angle(degrees: Double(currentZone.labelRotationAngleModifier * currentZone.intensity.count / 2))) .opacity(isScrolling ? 0 : 1) .animation(.easeInOut, value: isScrolling) WorkoutLevelPickerBackgroundView(total: total, lineWidth: lineWidth) withAnimation(.spring()) { WorkoutLevelPickerProgressView(total: total, completed: Int(completed), lineWidth: lineWidth, zone: currentZone) } VStack { Button { dismiss() } label: { Image(systemName: currentZone.iconName) .padding() .font(.title2) } .frame(width: 70, height: 70) } } .frame(width: 150, height: 150) .focusable() .digitalCrownRotation( detent: $completed.animation(.spring()), from: 1.0, through: 5.0, by: 1.0, sensitivity: .low, isContinuous: false, isHapticFeedbackEnabled: true, onChange: { _ in isScrolling = true }, onIdle: { isScrolling = false } ) .digitalCrownAccessory(.hidden) .onChange(of: completed) {_ in if(Int(completed) != currentZone.id) { currentZone = zones[Int(completed) - 1] print(currentZone.tint.description) } } } } }
问题演示

已尝试的解决方法
- 从其他视图展示Sheet
- 通过绑定而非
dismiss()动作关闭Sheet
两种方式均未解决问题,Sheet仍会在关闭后立即重新弹出。
补充说明:当Sheet弹出时,按侧边按钮可关闭它,应用会正常工作,直到用户尝试开始新运动。
内容的提问来源于stack exchange,提问作者Vlad Boancă
相关产品推荐
相关产品推荐

