为什么 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))
}
然后在 MainTabBarView
的 body
中创建一个 TabItem
将 从 更改为:
TabItem(currentIndex: $viewModel.index, tabIndex: 0, tab: .home)
到这个
TabItem(currentIndexIn: viewModel, at: \.index, tabIndex: 0, tab: .home)
由于缺少太多代码,我无法编译和测试它,所以这是一个猜测。我删除了丢失的代码只是为了消除它的错误,所以我可以说至少这个解决方案可以编译。需要在实际应用中进行应用和测试,以验证其是否有效。
无论如何,假设它确实有效,此解决方案仍然允许将索引保留在可观察对象中,而不依赖于该对象的特定类型。
我有带动画的 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))
}
然后在 MainTabBarView
的 body
中创建一个 TabItem
将 从 更改为:
TabItem(currentIndex: $viewModel.index, tabIndex: 0, tab: .home)
到这个
TabItem(currentIndexIn: viewModel, at: \.index, tabIndex: 0, tab: .home)
由于缺少太多代码,我无法编译和测试它,所以这是一个猜测。我删除了丢失的代码只是为了消除它的错误,所以我可以说至少这个解决方案可以编译。需要在实际应用中进行应用和测试,以验证其是否有效。
无论如何,假设它确实有效,此解决方案仍然允许将索引保留在可观察对象中,而不依赖于该对象的特定类型。