SwiftUI自定义深色模式下ScrollView滚动功能失效求助
SwiftUI自定义深色模式下ScrollView滚动冻结问题解决
问题描述
实现了不跟随系统的自定义深色模式,功能正常,但集成ScrollView后:
- 浅色模式下ScrollView滚动完全正常
- 深色模式下ScrollView滚动功能冻结,但组件颜色更新正常
代码结构:
- ScrollView位于
AllContactPage视图 AllContactPage在Home视图中调用- 深色模式切换按钮在
Menu视图中调用
问题代码
import SwiftUI let backgroundMWGradient = LinearGradient( colors: [Color.mint, Color.white], startPoint: .topLeading, endPoint: .bottomTrailing ) // 补充原代码缺失的深色背景渐变 let backgroundMBGradient = LinearGradient( colors: [Color.black, Color.blue], startPoint: .topLeading, endPoint: .bottomTrailing ) struct MainView: View { var body: some View { Home() } } struct MainView_Previews: PreviewProvider { static var previews: some View { MainView() } } struct Home : View { @State var dark = false @State var show = false var body: some View { ZStack(alignment: .leading) { dark ? backgroundMBGradient.edgesIgnoringSafeArea(.all) : backgroundMWGradient.edgesIgnoringSafeArea(.all) GeometryReader {_ in VStack { ZStack { HStack { Button(action: { withAnimation(.default) { self.show.toggle() } }) { Image(systemName: "chevron.forward.2") .font(.title) } Spacer() } } .padding() .foregroundColor(.primary) .overlay(Rectangle().stroke(Color.primary.opacity(0.1), lineWidth: 1).shadow(radius: 3).edgesIgnoringSafeArea(.top)) Spacer() AllContactPage(dark: self.$dark) Spacer() } } HStack { Menu(dark: self.$dark, show: self.$show) .preferredColorScheme(self.dark ? .dark : .light) .offset(x: self.show ? 0 : -UIScreen.main.bounds.width / 1.5) Spacer(minLength: 0) } .background(Color.primary.opacity(self.show ? (self.dark ? 0.05 : 0.2) : 0).edgesIgnoringSafeArea(.all)) } } } struct Menu : View { @Binding var dark : Bool @Binding var show : Bool var body: some View { VStack { HStack { Button(action: { withAnimation(.default) { self.show.toggle() } }) { Image(systemName: "chevron.backward.2") .font(.title) } } .padding(.top) .padding(.bottom, 25) HStack(spacing: 22) { Image(systemName: dark ? "moon.fill" : "sun.max.fill") .font(.title) Text("Mode") Spacer() Button(action: { self.dark.toggle() if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene { windowScene.windows.first?.overrideUserInterfaceStyle = self.dark ? .dark : .light } }) { Image(systemName: "switch.2") .font(.title) .rotationEffect(.init(degrees: self.dark ? 180 : 0)) } } .padding(.top, 25) } .foregroundColor(.primary) .padding(.horizontal, 20) .frame(width: UIScreen.main.bounds.width / 1.5) .background((self.dark ? backgroundMBGradient : backgroundMWGradient).edgesIgnoringSafeArea(.all)) .overlay(Rectangle().stroke(Color.primary.opacity(0.2), lineWidth: 2).shadow(radius: 3).edgesIgnoringSafeArea(.all)) } } // Rectangle with contact ScrollView in it struct AllContactPage : View { @Binding var dark : Bool var body: some View { VStack { ZStack(alignment: .top) { Rectangle() .cornerRadius(8.0) .frame(height: 500.0) .foregroundColor(Color.white) .padding(.all) .shadow(color: Color.black.opacity(0.5), radius: 5, x: 0, y: 0) .blendMode(.overlay) .opacity(0.4) VStack { ScrollView(showsIndicators: true) { VStack { ForEach(0 ..< 10) { index in Rectangle() .cornerRadius(8.0) .frame(height: 80.0) .foregroundColor(Color.white) .shadow(color: Color.black.opacity(0.5), radius: 5, x: 0, y: 0) .blendMode(.overlay) .opacity(0.8) if (index != 9) { Divider() .overlay(dark ? .white : .gray) } } } .padding() } .padding() } .padding() } } } }
修复方案
1. 移除干扰触摸的混合模式
.blendMode(.overlay)会在深色模式下干扰SwiftUI的触摸事件识别,导致ScrollView无法响应滚动。移除以下两处的该修饰符:
AllContactPage中背景Rectangle的.blendMode(.overlay)- 列表项Rectangle的
.blendMode(.overlay)
2. 统一设置颜色Scheme
将preferredColorScheme从Menu视图移到Home的根ZStack上,确保整个视图层级使用统一的颜色Scheme,避免样式冲突:
struct Home : View { @State var dark = false @State var show = false var body: some View { ZStack(alignment: .leading) { // ... 原有代码 ... } .preferredColorScheme(dark ? .dark : .light) // 移到根视图 } }
同时删除Menu视图上的.preferredColorScheme修饰符。
3. 适配深色模式的动态颜色
将固定的Color.white替换为动态系统颜色,自动适配深浅模式:
- 背景Rectangle使用
Color(UIColor.systemBackground) - 列表项Rectangle使用
Color(UIColor.secondarySystemBackground)
修改后的完整代码
import SwiftUI let backgroundMWGradient = LinearGradient( colors: [Color.mint, Color.white], startPoint: .topLeading, endPoint: .bottomTrailing ) let backgroundMBGradient = LinearGradient( colors: [Color.black, Color.blue], startPoint: .topLeading, endPoint: .bottomTrailing ) struct MainView: View { var body: some View { Home() } } struct MainView_Previews: PreviewProvider { static var previews: some View { MainView() } } struct Home : View { @State var dark = false @State var show = false var body: some View { ZStack(alignment: .leading) { dark ? backgroundMBGradient.edgesIgnoringSafeArea(.all) : backgroundMWGradient.edgesIgnoringSafeArea(.all) GeometryReader {_ in VStack { ZStack { HStack { Button(action: { withAnimation(.default) { self.show.toggle() } }) { Image(systemName: "chevron.forward.2") .font(.title) } Spacer() } } .padding() .foregroundColor(.primary) .overlay(Rectangle().stroke(Color.primary.opacity(0.1), lineWidth: 1).shadow(radius: 3).edgesIgnoringSafeArea(.top)) Spacer() AllContactPage() Spacer() } } HStack { Menu(dark: self.$dark, show: self.$show) .offset(x: self.show ? 0 : -UIScreen.main.bounds.width / 1.5) Spacer(minLength: 0) } .background(Color.primary.opacity(self.show ? (self.dark ? 0.05 : 0.2) : 0).edgesIgnoringSafeArea(.all)) } .preferredColorScheme(dark ? .dark : .light) } } struct Menu : View { @Binding var dark : Bool @Binding var show : Bool var body: some View { VStack { HStack { Button(action: { withAnimation(.default) { self.show.toggle() } }) { Image(systemName: "chevron.backward.2") .font(.title) } } .padding(.top) .padding(.bottom, 25) HStack(spacing: 22) { Image(systemName: dark ? "moon.fill" : "sun.max.fill") .font(.title) Text("Mode") Spacer() Button(action: { self.dark.toggle() if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene { windowScene.windows.first?.overrideUserInterfaceStyle = self.dark ? .dark : .light } }) { Image(systemName: "switch.2") .font(.title) .rotationEffect(.init(degrees: self.dark ? 180 : 0)) } } .padding(.top, 25) } .foregroundColor(.primary) .padding(.horizontal, 20) .frame(width: UIScreen.main.bounds.width / 1.5) .background((self.dark ? backgroundMBGradient : backgroundMWGradient).edgesIgnoringSafeArea(.all)) .overlay(Rectangle().stroke(Color.primary.opacity(0.2), lineWidth: 2).shadow(radius: 3).edgesIgnoringSafeArea(.all)) } } struct AllContactPage : View { var body: some View { VStack { ZStack(alignment: .top) { Rectangle() .cornerRadius(8.0) .frame(height: 500.0) .foregroundColor(Color(UIColor.systemBackground)) .padding(.all) .shadow(color: Color.black.opacity(0.5), radius: 5, x: 0, y: 0) .opacity(0.4) VStack { ScrollView(showsIndicators: true) { VStack { ForEach(0 ..< 10) { index in Rectangle() .cornerRadius(8.0) .frame(height: 80.0) .foregroundColor(Color(UIColor.secondarySystemBackground)) .shadow(color: Color.black.opacity(0.5), radius: 5, x: 0, y: 0) .opacity(0.8) if index != 9 { Divider() } } } .padding() } .padding() } .padding() } } } }
说明
- 移除
.blendMode(.overlay)后,ScrollView的触摸事件识别恢复正常 - 统一颜色Scheme设置避免了视图层级的样式冲突
- 使用动态系统颜色后,视图会自动适配深浅模式,无需手动切换颜色
内容的提问来源于stack exchange,提问作者Pyg Argus
相关产品推荐
相关产品推荐

