更改主题时 SwiftUI 工作表大小变为 0(iOS 16 bug?)

问题描述 投票:0回答:1

我遇到了一个奇怪的 SwiftUI bug。我有一张工作表,应该根据其内容自行调整大小(使用 PreferenceKey + GeometryReader)。一切正常,直到我尝试使用 overrideUserInterfaceStyle 更改用户界面样式。

当我像这样改变主题时,工作表大小突然变成0,甚至没有调用onPreferenceChange。这只发生在 iOS 16 上。

您可以在屏幕录像中看到问题。

iOS 16.4 iOS 18.1

import SwiftUI

fileprivate struct IntrinsicContentSizePreferenceKey: PreferenceKey {
    static let defaultValue: CGSize = .zero
    
    static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
        value = nextValue()
    }
}

fileprivate struct FittedPresentationDetentModifier: ViewModifier {
    @State private var contentSize: CGSize = .zero

    func body(content: Content) -> some View {
        content
            .overlay(
                GeometryReader { proxy in
                    Color.clear.preference(
                        key: IntrinsicContentSizePreferenceKey.self,
                        value: proxy.size
                    )
                }
            )
            .onPreferenceChange(IntrinsicContentSizePreferenceKey.self) { newSize in
                print("→ current size: \(contentSize)")
                print("→ new size: \(newSize)")
                guard newSize != .zero else { return }
                contentSize = newSize
            }
            .presentationDetents([.height(contentSize.height)])
            .presentationCornerRadius(32.0)
    }
}

fileprivate extension View {
    func fittedPresentationDetent() -> some View {
        modifier(FittedPresentationDetentModifier())
    }
}

extension View {
    func bottomSheet<Content: View>(
        isPresented: Binding<Bool>,
        @ViewBuilder content: @escaping () -> Content
    ) -> some View {
        self.sheet(isPresented: isPresented) {
            content()
                .fittedPresentationDetent()
        }
    }
}

我创建了一个示例项目用于测试。

import SwiftUI

struct ContentView: View {
    @State private var showSheet = false
    @Environment(\.colorScheme) var colorScheme
    
    var isDarkMode: Bool {
        colorScheme == .dark
    }
    
    var body: some View {
        NavigationView {
            VStack(spacing: 20) {
                Image(systemName: isDarkMode ? "moon.fill" : "sun.max.fill")
                    .font(.system(size: 60))
                    .foregroundColor(isDarkMode ? .white : .yellow)
                    .padding()
                
                Text("Current Theme: \(isDarkMode ? "Dark" : "Light")")
                    .font(.headline)
                
                Button(action: {
                    showSheet.toggle()
                }) {
                    Text("Change Theme")
                        .font(.headline)
                        .padding()
                        .background(Color.blue)
                        .foregroundColor(.white)
                        .cornerRadius(10)
                }
            }
            .navigationTitle("Theme Switcher")
            .bottomSheet(isPresented: $showSheet, content: {
                ThemeSettingsSheet(isDarkMode: isDarkMode)
            })
        }
    }
}

struct ThemeSettingsSheet: View {
    let isDarkMode: Bool
    @Environment(\.dismiss) var dismiss
    
    var body: some View {
        Toggle(isOn: Binding(
            get: { isDarkMode },
            set: { newValue in
                changeTheme(isDarkMode: newValue)
            }
        )) {
            HStack {
                Image(systemName: isDarkMode ? "moon.fill" : "sun.max.fill")
                    .foregroundColor(isDarkMode ? .white : .yellow)
                Text("Dark Mode")
            }
        }
        .padding()
    }
    
    private func changeTheme(isDarkMode: Bool) {
        guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
              let window = windowScene.windows.first else { return }
        
        UIView.transition(with: window,
                          duration: 0.3,
                          options: .transitionCrossDissolve,
                          animations: {
            window.overrideUserInterfaceStyle = isDarkMode ? .dark : .light
        }, completion: nil)
    }
}

#Preview {
    ContentView()
}

有人遇到过这种情况吗?有什么解决方法吗?谢谢!

ios swiftui
1个回答
0
投票

您可以将

.presentationDetents
设置为
.presentationBackground
,而不是设置
Color.clear
。然后让您的内容找到自己的大小,并为其指定
UnevenRoundedRectangle
作为背景。

这样做,既不需要

GeometryReader
也不需要
PreferenceKey

fileprivate struct FittedPresentationDetentModifier: ViewModifier {
    func body(content: Content) -> some View {
        content
            .background {
                UnevenRoundedRectangle(topLeadingRadius: 32, topTrailingRadius: 32)
                    .fill(.background)
                    .ignoresSafeArea(edges: .bottom)
            }
            .frame(maxHeight: .infinity, alignment: .bottom)
            .presentationBackground(.clear)
    }
}

顺便说一句,可能会有更简单的方法来更改配色方案,请参阅Sweeper的评论。

© www.soinside.com 2019 - 2024. All rights reserved.