【问题标题】:Set TabBar Item badge count with SwiftUI使用 SwiftUI 设置 TabBar 项徽章计数
【发布时间】:2020-04-03 11:50:53
【问题描述】:

是否可以使用 SwiftUI 显示 TabItem 徽章?

使用 UIKit 很容易实现,就像这里描述的那样 ->

How to set badge value in Tab bar?

我没有找到使用 SwiftUI 的方法。 唯一可行的方法是使用场景 rootViewController 访问 UITabBarController 并直接修改其标签栏项。

  func setBadgeCount(_ count: Int) {
    UIApplication.shared.applicationIconBadgeNumber = count

    guard let delegate = app.connectedScenes.first?.delegate as? SceneDelegate else {
        return
    }

    if let tabBarController = delegate.window?.rootViewController?.children.first {
      tabBarController.viewControllers?.first?.tabBarItem.badgeValue = "\(count)"
    }
  }

任何想法如何使用原生 SwiftUI 方法做到这一点?

【问题讨论】:

标签: swift uitabbarcontroller swiftui uitabbar uitabbaritem


【解决方案1】:

iOS 15 增加了对 .badge 修饰符的支持,但由于我需要支持 iOS 14,所以我创建了 UITabBarController 包装器:

struct TabBarController<TabContent: View, Tab: Hashable>: UIViewControllerRepresentable {
    let tabs: [Tab]
    @Binding
    var selection: Tab
    let tabBarItem: (Tab) -> UITabBarItem
    let badgeValue: (Tab) -> String?
    @ViewBuilder
    let contentView: (Tab) -> TabContent

    func makeUIViewController(context: Context) -> UITabBarController {
        let controller = UITabBarController()
        controller.delegate = context.coordinator
        return controller
    }

    func updateUIViewController(_ uiViewController: UITabBarController, context: Context) {
        context.coordinator.viewControllers
            .keys
            .filterNot(tabs.contains(_:))
            .forEach { removedKey in
                context.coordinator.viewControllers.removeValue(forKey: removedKey)
            }
        uiViewController.viewControllers = tabs.map { tab in
            let rootView = contentView(tab)
            let viewController = context.coordinator.viewControllers[tab] ?? {
                let viewController = UIHostingController(rootView: rootView)
                viewController.tabBarItem = tabBarItem(tab)
                context.coordinator.viewControllers[tab] = viewController
                return viewController
            }()
            viewController.rootView = rootView
            viewController.tabBarItem.badgeValue = badgeValue(tab)
            return viewController
        }
        uiViewController.selectedIndex = tabs.firstIndex(of: selection) ?? 0
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(selection: $selection)
    }

    final class Coordinator: NSObject, UITabBarControllerDelegate {
        @Binding
        private var selection: Tab
        var viewControllers = [Tab: UIHostingController<TabContent>]()

        init(selection: Binding<Tab>) {
            _selection = selection
        }

        func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
            guard let newTab = viewControllers.first(where: { $0.value == viewController })?.key else {
                print("tabBarController:didSelect: unexpected")
                return
            }
            selection = newTab
        }
    }
}

用法:

TabBarController(
    tabs: [1,2,3],
    selection: $selection,
    tabBarItem: { tab in
        UITabBarItem(title: "tab \(tab)", image: UIImage(systemName: "1.square.fill"), tag: 0)
    },
    badgeValue: { tab in
        "\(tab)"
    },
    contentView: { tab in
        Text("\(tab) screen")
    }
).ignoresSafeArea()

【讨论】:

    【解决方案2】:

    上面提到的 .introspectTabBarController 修饰符对我有用。 在 tabItem 上给了一个不错的原生徽章。在两个方向上看起来都很棒。

        .introspectTabBarController { (UITabBarController) in
            self.tabBarControl = UITabBarController
            if let items = UITabBarController.tabBar.items {
                let tabItem = items[2] // in my case it was 3rd item
                tabItem.badgeValue = "5" // hardcoded
            }
        }
    

    不过,当更改标签时,徽章消失了,所以我将 UITabBarController 保存在 @State 中,当标签更改时,我再次设置 tabItem.badgeValue。

    P.S:iOS 15+ 支持 .badge 修饰符。如果您的目标是以上或 iOS 15,请使用它。

    【讨论】:

      【解决方案3】:

      现在在 SwiftUI 3 中,他们添加了 .badge() 修饰符

      来源:HackingWithSwift

      TabView {
          Text("Your home screen here")
              .tabItem {
                  Label("Home", systemImage: "house")
              }
              .badge(5)
      }
      

      【讨论】:

        【解决方案4】:
        struct ContentView: View {
        var body: some View {
            TabView {
                Text("Home")
                    .tabItem {
                        Text("Home")
                    }
                
                Text("Home")
                    .tabItem {
                        Text("Home")
                    }
                
                Text("Home")
                    .tabItem {
                        Text("Home")
                    }
            }
            .introspectTabBarController { (tabbarController) in
                if let items = tabbarController.tabBar.items {
                    let tabItem = items[2]
                    tabItem.badgeValue = "1"
                }
            }
        }
        

        }

        【讨论】:

          【解决方案5】:
           func calculateBadgeXPos(width: CGFloat) -> CGFloat {
                  let t = (2*CGFloat(self.selectedTab))+1
                  return CGFloat(t * width/(2*CGFloat(TABS_COUNT)))
              }
          

          然后在这里使用它:

          GeometryReader { geometry in
                      ZStack(alignment: .bottomLeading) {
                          // make sure to update TABS_COUNT
                          TabView(selection: self.$selectedTab) {
                             ...
                          }
                          
                          NotificationBadge(...)
                          .offset(x: self.calculateBadgeXPos(width: geometry.size.width), y: -28)
                      }
                  }
          
          
          
          

          在Preview上看起来像这样

          【讨论】:

            【解决方案6】:

            目前,SwiftUI 没有徽章功能,所以我们必须自定义。

            参考HERE我用徽章创建我的标签

            struct ContentView: View {
                private var badgePosition: CGFloat = 2
                private var tabsCount: CGFloat = 2
                @State var selectedView = 0
                var body: some View {
                    GeometryReader { geometry in
                        ZStack(alignment: .bottomLeading) {
                            TabView {
                                Text("First View")
                                    .tabItem {
                                        Image(systemName: "list.dash")
                                        Text("First")
                                    }.tag(0)
                                Text("Second View")
                                    .tabItem {
                                        Image(systemName: "star")
                                        Text("Second")
                                    }.tag(1)
                            }
            
                            ZStack {
                              Circle()
                                .foregroundColor(.red)
            
                                Text("3")
                                .foregroundColor(.white)
                                .font(Font.system(size: 12))
                            }
                            .frame(width: 15, height: 15)
                            .offset(x: ( ( 2 * self.badgePosition) - 0.95 ) * ( geometry.size.width / ( 2 * self.tabsCount ) ) + 2, y: -30)
                            .opacity(1.0)
                        }
                    }
                }
            }
            

            【讨论】:

            • 感谢您提供的解决方案!遗憾的是,Apple 没有为 SwiftUI 添加对标签栏项目徽章的支持。
            • 这是通用功能,我认为 SwiftUI 会支持它,也许在下一个版本中会支持它
            • 如果您同时为 iPad 和 iPhone 进行编译,您可以将 0.95 in offset 替换为 (UIDevice.current.userInterfaceIdiom == .phone ? 0.95 : 1.65)
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2014-08-25
            • 1970-01-01
            • 1970-01-01
            • 2017-05-14
            • 2010-11-11
            • 2016-03-21
            • 1970-01-01
            相关资源
            最近更新 更多