将样式重新应用到 UITabBarItems,无需重新加载应用程序

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

我有让用户更改

Font.Design
的逻辑,这对于 SwiftUI 视图以及导航栏(下面包含的逻辑)都适用。但它不会重新加载或重新应用到现有的
TabView
工具栏,需要重新加载。实时更新视图的最佳方式是什么?

@MainActor
private func configureTabBars(with fontDesign: Font.Design) {
    UINavigationBar.appearance().largeTitleTextAttributes = [
        .font : UIFont(descriptor: UIFontDescriptor.preferredFontDescriptor(withTextStyle: .caption1).withDesign(fontDesign.systemDesign)!, size: 0)
    ]
    UITabBarItem.appearance().setTitleTextAttributes([
        .font : UIFont(descriptor: UIFontDescriptor.preferredFontDescriptor(withTextStyle: .caption1).withDesign(fontDesign.systemDesign)!, size: 0)
    ], for: [])
}

private extension Font.Design {
    var systemDesign: UIFontDescriptor.SystemDesign {
        switch self {
        case .serif: .serif
        case .rounded: .rounded
        case .monospaced: .monospaced
        default: .default
        }
    }
}

swiftui uinavigationbar uitabbaritem uifont
1个回答
0
投票

这个问题类似,

appearance()
不起作用的原因是它不会影响已经添加到窗口视图层次结构中的视图。

与链接的问题类似,这可以通过使用

UIViewControllerRepresentable
并从我们控制的视图控制器中找到
UINavigationController
UITabBarItem
来完成。

struct BarAppearances: UIViewControllerRepresentable {
    let fontDesign: Font.Design
    // instead of passing a Font.Design through the initialiser like above,
    // also consider injecting it into the environment, so you can read it directly like
    // @Environment(\.fontDesign) var fontDesign
    
    class VC: UIViewController {
        var fontDesign: Font.Design = .default
        
        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(animated)
            updateBarAppearances()
        }
        
        func updateBarAppearances() {
            let font = UIFont(descriptor: UIFontDescriptor.preferredFontDescriptor(withTextStyle: .caption1).withDesign(fontDesign.systemDesign)!, size: 0)
            
            // here I am using the new UIBarAppearance APIs, but the old 'largeTitleTextAttributes' should work too
            let navAppearance = UINavigationBarAppearance()
            navAppearance.configureWithOpaqueBackground()
            navAppearance.largeTitleTextAttributes = [.font: font]
            
            findImmediateChildOfTabVC()?.tabBarItem.setTitleTextAttributes([.font: font], for: [])
            
            navigationController?.navigationBar.standardAppearance = navAppearance
            navigationController?.navigationBar.scrollEdgeAppearance = navAppearance
            navigationController?.navigationBar.compactAppearance = navAppearance
            navigationController?.navigationBar.compactScrollEdgeAppearance = navAppearance
            
        }
        
        // the UITabBarItem we want to modify is the immediate child of the UITabBarController
        // changing the tabBarItems of other VCs won't have an effect
        func findImmediateChildOfTabVC() -> UIViewController? {
            var vc: UIViewController? = self
            while vc != nil && !(vc?.parent is UITabBarController) {
                vc = vc?.parent
            }
            return vc
        }
    }
    
    func makeUIViewController(context: Context) -> VC {
        VC()
    }
    
    func updateUIViewController(_ uiViewController: VC, context: Context) {
        uiViewController.fontDesign = fontDesign
        uiViewController.updateBarAppearances()
    }
}

要使用它,请将其作为根视图的

background
放在
NavigationStack
中:

struct ContentView: View {
    @State var id = UUID()
    @State var design = Font.Design.default
    
    var body: some View {
        TabView {
            NavigationStack {
                VStack {
                    Button("Set to monospace") {
                        design = .monospaced
                    }
                    Button("Set to rounded") {
                        design = .rounded
                    }
                }
                .navigationTitle("Some Nav Title")
                .background { BarAppearances(fontDesign: design) }
            }
            .tabItem {
                Text("Some Tab Title")
            }
        }
    }
}

解决此问题的另一种方法是通过更改

id
TabView
来简单地重新创建整个视图层次结构。

这是一个非常简单的例子

@State var id = UUID()
var body: some View {
    TabView {
        NavigationStack {
            VStack {
                Button("Set to monospace") {
                    configureTabBars(with: .monospaced)
                    id = UUID()
                }
                Button("Set to rounded") {
                    configureTabBars(with: .rounded)
                    id = UUID()
                }
            }
            .navigationTitle("Some Nav Title")
        }
        .tabItem {
            Text("Some Tab Title")
        }
    }
    .id(id)
}

这样做的问题是各个选项卡的所有

@State
都会丢失。不过,由于这改变了应用程序的主题,作为用户,我个人并不介意。

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