【问题标题】:SwiftUI custom TabView with paging style具有分页样式的 SwiftUI 自定义 TabView
【发布时间】:2023-02-02 16:52:53
【问题描述】:

我正在尝试在 SwiftUI 中创建自定义 TabView,它也具有 .tabViewStyle(.page()) 功能。

目前我已经完成了 99%,但无法弄清楚如何将所有 TabBarItems 列出。

我正在使用 PreferenceKey,这样我将它们添加到闭包中的顺序就是 TabView 中的顺序。

当我运行它时,选项卡项被添加到数组中,然后被删除,但它似乎不起作用。

我使用枚举作为CaseIterable,将ForEach(tabs) { tab in作为ForEach(TabBarItems.allCases) { tab in使用,但如前所述,我希望栏中的顺序是来自clousure的有机顺序。

容器

struct TabViewContainer<Content : View>: View {
  @Binding private var selection: TabBarItem
  @State private var tabs: [TabBarItem] = []
  var content: Content

  init(selection: Binding<TabBarItem>, @ViewBuilder content: () -> Content) {
    self._selection = selection
    self.content = content()
  }

  var body: some View {
    ZStack(alignment: .bottom) {
      TabView(selection: $selection) {
        content
      }
      .tabViewStyle(.page(indexDisplayMode: .never))
      tabBarItems()
    }
    .onPreferenceChange(TabBarItemsPreferenceKey.self) { self.tabs = $0 }
  }

  private func tabBarItems() -> some View {
    HStack(spacing: 10) {
      ForEach(tabs) { tab in
        Button {
          selection = tab
        } label: {
          tabButton(tab: tab)
        }
      }
    }
    .padding(.horizontal)
    .frame(maxWidth: .infinity)
    .padding(.top, 8)
    .background(Color(uiColor: .systemGray6))
  }

  private func tabButton(tab: TabBarItem) -> some View {
    VStack(spacing: 0) {
      Image(icon: tab.icon)
        .font(.system(size: 16))
        .frame(maxWidth: .infinity, minHeight: 28)
      Text(tab.title)
        .font(.system(size: 10, weight: .medium, design: .rounded))
    }
    .foregroundColor(selection == tab ? tab.colour : .gray)
  }
}

首选项键/修饰符

struct TabBarItemsPreferenceKey: PreferenceKey {
  static var defaultValue: [TabBarItem] = []
  static func reduce(value: inout [TabBarItem], nextValue: () -> [TabBarItem]) {
    value += nextValue()
  }
}

struct TabBarItemViewModifier: ViewModifier {
  let tab: TabBarItem
  func body(content: Content) -> some View {
    content.preference(key: TabBarItemsPreferenceKey.self, value: [tab])
  }
}

extension View {
  func tabBarItem(_ tab: TabBarItem) -> some View {
    modifier(TabBarItemViewModifier(tab: tab))
  }
}

演示视图

struct TabSelectionView: View {
  @State private var selection: TabBarItem = .itinerary
  var body: some View {
    TabViewContainer(selection: $selection) {
      PhraseView()
        .tabBarItem(.phrases)
      ItineraryView()
        .tabBarItem(.itinerary)
      BudgetView()
        .tabBarItem(.budget)
      BookingView()
        .tabBarItem(.bookings)
      PackingListView()
        .tabBarItem(.packing)
    }
  }
}
Intended Current

【问题讨论】:

    标签: ios swift swiftui swiftui-tabview


    【解决方案1】:

    你可以使用更优雅的方式,@resultBuilder

    1. 您创建了一个包含View 和标签的结构;
    2. tabBarItem 现在应该返回之前创建的结构;
    3. @resultBuilder 然后将构建您将在容器内使用的视图和标记数组。

      结果生成器:

      @resultBuilder
      public struct TabsBuilder {
          internal static func buildBlock(_ components: Tab...) -> [Tab] {
              return components
          }
          internal static func buildEither(first component: Tab) -> Tab {
              return component
          }
          internal static func buildEither(second component: Tab) -> Tab {
              return component
          }
      }
      

      标签:

      struct Tab: Identifiable {
          var content: AnyView //I don't recommend the use of AnyView, but I don't want to dive deep into generics for now.
          var tag: TabBarItem
          var id = UUID()
      }
      

      修改器:

      struct Tab: Identifiable {
          var content: AnyView
          var tag: TabBarItem
          var id = UUID()
      }
      

      标签视图容器:

      struct TabViewContainer: View {
          @Binding private var selection: TabBarItem
          @State private var tabs: [TabBarItem] = TabBarItem.allCases
          var content: [Tab]
          init(selection: Binding<TabBarItem>, @TabsBuilder content: () -> [Tab]) {
              self._selection = selection
              self.content = content()
          }
          var body: some View {
              ZStack(alignment: .bottom) {
                  TabView(selection: $selection) {
                      ForEach(content) { content in
                          content.content
                              .tag(content.tag)
                      }
                  }.tabViewStyle(.page(indexDisplayMode: .never))
                  tabBarItems()
              }
          }
          private func tabBarItems() -> some View {
              HStack(spacing: 10) {
                  ForEach(tabs) { tab in
                      Button {
                          selection = tab
                      } label: {
                          tabButton(tab: tab)
                      }
                  }
              }
              .padding(.horizontal)
              .frame(maxWidth: .infinity)
              .padding(.top, 8)
              .background(Color(uiColor: .systemGray6))
          }
          private func tabButton(tab: TabBarItem) -> some View {
              VStack(spacing: 0) {
                  Image(icon: tab.icon)
                      .font(.system(size: 16))
                      .frame(maxWidth: .infinity, minHeight: 28)
                  Text(tab.title)
                      .font(.system(size: 10, weight: .medium, design: .rounded))
              }
              .foregroundColor(selection == tab ? tab.colour : .gray)
          }
      }
      

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-01-26
      • 1970-01-01
      • 2017-02-22
      • 2020-05-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-08
      相关资源
      最近更新 更多