If you need to have the ability to specify a Transition
for every of the tabs utilizing a view modifier, such as you did within the instance code, it’s best to write down your personal TabView
.
Right here is an tailored model of the customized “tab view” I wrote right here, that makes the tabs look extra like tabs. I’ve additionally added a tabTransition
modifier to let you select a Transition
.
struct CustomTabView<Content material: View, Choice: Hashable>: View {
@Binding var selectedTab: Choice
@ViewBuilder let content material: () -> Content material
var physique: some View {
Extract(content material) { views in
ForEach(views) { view in
if view.id(as: Choice.self) == selectedTab {
view
.body(maxWidth: .infinity, maxHeight: .infinity)
.transition(view[TabTransitionTrait.self])
}
}
}
.safeAreaInset(edge: .backside) {
HStack {
Spacer()
ExtractMulti(content material) { views in
ForEach(views) { view in
Group {
if let label = view[CustomTabItemTrait.self] {
label
} else {
Textual content("Unnamed")
}
}
.onTapGesture {
if let choice = view.id(as: Choice.self) {
selectedTab = choice
}
}
.foregroundStyle(
view.id(as: Choice.self) == selectedTab ?
AnyShapeStyle(Coloration.accentColor) : AnyShapeStyle(.opacity(1))
)
Spacer()
}
}
}
}
}
}
extension View {
func customTabItem<Content material: View>(@ViewBuilder content material: () -> Content material) -> some View {
_trait(CustomTabItemTrait.self, AnyView(content material()))
}
}
struct CustomTabItemTrait: _ViewTraitKey {
static let defaultValue: AnyView? = nil
}
extension View {
func tabTransition<T: Transition>(_ transition: T) -> some View {
_trait(TabTransitionTrait.self, AnyTransition(transition))
}
}
struct TabTransitionTrait: _ViewTraitKey {
static let defaultValue: AnyTransition = .identification
}
// MARK: View Extractor - https://github.com/GeorgeElsham/ViewExtractor
public struct Extract<Content material: View, ViewsContent: View>: View {
let content material: () -> Content material
let views: (Views) -> ViewsContent
public init(_ content material: Content material, @ViewBuilder views: @escaping (Views) -> ViewsContent) {
self.content material = { content material }
self.views = views
}
public init(@ViewBuilder _ content material: @escaping () -> Content material, @ViewBuilder views: @escaping (Views) -> ViewsContent) {
self.content material = content material
self.views = views
}
public var physique: some View {
_VariadicView.Tree(
UnaryViewRoot(views: views),
content material: content material
)
}
}
fileprivate struct UnaryViewRoot<Content material: View>: _VariadicView_UnaryViewRoot {
let views: (Views) -> Content material
func physique(kids: Views) -> some View {
views(kids)
}
}
public struct ExtractMulti<Content material: View, ViewsContent: View>: View {
let content material: () -> Content material
let views: (Views) -> ViewsContent
public init(_ content material: Content material, @ViewBuilder views: @escaping (Views) -> ViewsContent) {
self.content material = { content material }
self.views = views
}
public init(@ViewBuilder _ content material: @escaping () -> Content material, @ViewBuilder views: @escaping (Views) -> ViewsContent) {
self.content material = content material
self.views = views
}
public var physique: some View {
_VariadicView.Tree(
MultiViewRoot(views: views),
content material: content material
)
}
}
fileprivate struct MultiViewRoot<Content material: View>: _VariadicView_MultiViewRoot {
let views: (Views) -> Content material
func physique(kids: Views) -> some View {
views(kids)
}
}
public typealias Views = _VariadicView.Kids
Instance utilization:
struct ContentView: View {
@State var selectedTab = 0
var physique: some View {
CustomTabView(selectedTab: $selectedTab.animation()) {
Coloration.blue
.id(0) // you could use .id as a substitute of .tag to specify the choice worth
.customTabItem {
Label("Baz", systemImage: "circle")
}
.tabTransition(.push(from: .main))
Coloration.yellow
.id(1)
.customTabItem {
Label("Foo", systemImage: "globe")
}
.tabTransition(.push(from: .backside))
Coloration.inexperienced
.id(2)
.customTabItem {
Label("Bar", systemImage: "rectangle")
}
.tabTransition(.opacity)
}
}
}