为什么 swift 视图不随 UIKit 更新?

Why swift view does not update with UIKit?

我有带动画的 swiftUI 标签栏,我还有包含这个 swiftUI 视图的 UITabbarViewController。

SwiftUI

struct MainTabBarView: View {
    
    @ObservedObject var viewModel: MainTabBarViewModel
    
    var body: some View {
        VStack {
        HStack(spacing: 0) {
            TabItem(currentIndex: $viewModel.index, tabIndex: 0, tab: .home)
            Spacer()
            TabItem(currentIndex: $viewModel.index, tabIndex: 1, tab: .search)
            Spacer()
            TabItem(currentIndex: $viewModel.index, tabIndex: 2, tab: .library)
        }.padding(.top, 8).padding(.leading, 28).padding(.trailing, 28)
        .padding(.bottom, 8)
        .frame(width: UIScreen.main.bounds.width)
        .animation(.easeIn(duration: 0.2))

}

UITabbarViewController:

final class MainTabBarViewController: UITabBarController, Navigatable {
         private var viewModel = MainTabBarViewModel()

        // MARK: - Lifecycle
        override func viewDidLoad() {
            super.viewDidLoad()
            bindViewModel()
        }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        configTabBarView()
    }
 
     
        private func configTabBarView() {
            let view = UIHostingController(rootView: MainTabBarView(viewModel: viewModel))
            addChild(child)
            tabBar.addSubview(view.view)
            view.didMove(toParent: self)
            tabBar.setValue(true, forKey: "hidesShadow")
            view.view.snp.makeConstraints { make in
                make.leading.trailing.top.equalToSuperview()
                make.height.equalTo(100)
            }
         
        }
 

视图模型:

final class MainTabBarViewModel: NSObject, ObservableObject {
   
   @Published var index: Int = 0 
}

这是我的带有选项卡的自定义视图的代码。 当我按下选项卡时 -> 来自 viewModel 的索引在此视图中发生变化,然后

struct TabItem: View {
    
    @Binding var currentIndex: Int
    var tabIndex: Int
    var isCurrentTab: Bool {
        return currentIndex == tabIndex
    }
    
    var tab: Tabs
    
    var body: some View {
        HStack {
            Image(uiImage: tab.icon).resizable().frame(width: 25, height: 25, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/)
                .foregroundColor(isCurrentTab ? R.color.primaryColor()!.swiftUI : R.color.grayScaleDark()!.swiftUI)
            Text(isCurrentTab ? tab.title : "").font(Font(R.font.poppinsRegular(size: 16)! as CTFont))
                .foregroundColor(isCurrentTab ? R.color.primaryColor()!.swiftUI : R.color.grayScaleDark()!.swiftUI)
        }.padding(15)
        .onTapGesture {
            self.currentIndex = tabIndex
           logWarn(String(tabIndex))
        }
        .background(self.tabIndex == currentIndex ? Color(R.color.pink()!) : Color.white)
        .frame(height: 44.0)
        .cornerRadius(22.0)
        .clipped()
    }
    
}

索引正在更改,但选项卡不更新视图的问题。看起来,好像视图没有更新。

虽然给定代码的编译会有所帮助,但我想我拼凑了一个解决方案,允许当前索引保留在 MainTabBarViewModel 中,假设它是为了保存其他的事情以后再说。我还假设 TabItem 应该足够通用,它不必特别依赖 MainTabVarViewModel

我的想法是基于在创建 TabItem 时提供当前索引的关键路径。如果不是因为 TabItem 更新当前索引就足够简单了。但是,因为它确实更新了它,所以在 onTapGesture 闭包中,编译器抱怨它无法通过 KeyPath 写入,因为 self 是不可变的。所以...召唤大卫·惠勒的鬼魂,我尝试通过保存在 TabItem.init...

首先 TabItem 成为通用的:

struct TabItem<TabBarViewModel: ObservableObject>: View {

    @ObservedObject var viewModel: TabBarViewModel
    let indexKeyPath: WritableKeyPath<TabBarViewModel, Int>
    let tapClosure: (Int) -> Void
    // @Binding var currentIndex: Int
    var currentIndex: Int { viewModel[keyPath: indexKeyPath] }

    ...

    init(currentIndexIn viewModel: TabBarViewModel, at indexKeyPath: WritableKeyPath<TabBarViewModel, Int>, tabIndex: Int, tab: Tabs)
    {
        self.viewModel = viewModel
        self.indexKeyPath = indexKeyPath
        self.tabIndex = tabIndex
        self.tab = tab
        self.tapClosure = { self.viewModel[keyPath: indexKeyPath] = [=10=] }
    }

TabItem 中的所有内容都可以编译(在删除对未提供代码的引用之后),除了 body

中的这一点
        .onTapGesture {
           currentIndex = tabIndex
           print(String(tabIndex))
        }

我将其更改为使用在 init:

中保存的闭包
        .onTapGesture {
           tapClosure(tabIndex)
           print(String(tabIndex))
        }

然后在 MainTabBarViewbody 中创建一个 TabItem 更改为:

TabItem(currentIndex: $viewModel.index, tabIndex: 0, tab: .home)

这个

TabItem(currentIndexIn: viewModel, at: \.index, tabIndex: 0, tab: .home)

由于缺少太多代码,我无法编译和测试它,所以这是一个猜测。我删除了丢失的代码只是为了消除它的错误,所以我可以说至少这个解决方案可以编译。需要在实际应用中进行应用和测试,以验证其是否有效。

无论如何,假设它确实有效,此解决方案仍然允许将索引保留在可观察对象中,而不依赖于该对象的特定类型。