skip to Main Content

I have sub-navigation inside Listview and trying to achieve leftSlide and rightSlide animation but it always shows default slide animation. I even tried to add .transition(.move(edge: . leading)) & .transition(.move(edge: . trailing))

import SwiftUI
import Combine

struct GameTabView: View {
    @State var selectedTab: Int = 0

    init() {
        UITableView.appearance().sectionHeaderTopPadding = 0
    }

    var body: some View {
        listView
            .ignoresSafeArea()
    }

    var listView: some View {
        List {
            Group {
                Color.gray.frame(height: 400)
                sectionView
            }
            .listRowInsets(EdgeInsets())
        }
        .listStyle(.plain)
    }
    var sectionView: some View {
        Section {
            tabContentView
                .transition(.move(edge: . leading)) // NOT WORKING
                .background(Color.blue)
        } header: {
            headerView
        }
    }

    private var headerView: some View {
        ScrollViewReader { proxy in
            ScrollView(.horizontal, showsIndicators: false) {
                HStack(spacing: 16) {
                    Button {
                        withAnimation {
                            selectedTab = 0
                        }
                    } label: {
                        Text("AAAA")
                            .padding()
                    }
                    Button {
                        withAnimation {
                            selectedTab = 1
                        }
                    } label: {
                        Text("BBBB")
                            .padding()
                    }
                    Button {
                        withAnimation {
                            selectedTab = 2
                        }
                    } label: {
                        Text("BBBB")
                            .padding()
                    }
                }
            }
        }
        .background(Color.green)
    }

    @ViewBuilder private var tabContentView: some View {
        switch selectedTab {
        case 0:
            DummyScreen(title: "FIRST", color: .red)
        case 1:
            DummyScreen(title: "SECOND", color: .green)
        case 2:
            DummyScreen(title: "THIRD", color: .blue)
        default:
            EmptyView()
        }
    }
}

struct DummyScreen: View {
    let title: String
    let color: Color
    var body: some View {
        VStack {
            ForEach(0..<15, id: .self) { index in
                HStack {
                    Text("#(index): title (title)")
                        .foregroundColor(Color.black)
                        .font(.system(size: 30))
                        .padding(.vertical, 20)
                    Spacer()
                }
                .background(Color.yellow)
            }
        }
        .background(color)
    }
}

enter image description here

2

Answers


  1. Chosen as BEST ANSWER

    Thanks to @tail and @Asperi

    I finally got the solution via updating ScrollView with ScrollviewProxy:

    import SwiftUI
    import Combine
    
    struct GameTabView: View {
        @State var selectedTab: Int = 0
        @State var proxy: ScrollViewProxy?
    
        init() {
            UITableView.appearance().sectionHeaderTopPadding = 0
        }
    
        var body: some View {
            listView
                .ignoresSafeArea()
        }
    
        var listView: some View {
            List {
                Group {
                    Color.gray.frame(height: 400)
                    sectionView
                }
                .listRowInsets(EdgeInsets())
            }
            .listStyle(.plain)
        }
        var sectionView: some View {
            Section {
                tabContentView
            } header: {
                headerView
            }
        }
    
        private var headerView: some View {
            ScrollViewReader { proxy in
                ScrollView(.horizontal, showsIndicators: false) {
                    HStack(spacing: 16) {
                        Button {
                            withAnimation {
                                selectedTab = 0
                                self.proxy?.scrollTo(0, anchor: .center)
                            }
                        } label: {
                            Text("AAAA")
                                .padding()
                        }
                        Button {
                            withAnimation {
                                selectedTab = 1
                                self.proxy?.scrollTo(1, anchor: .center)
                            }
                        } label: {
                            Text("BBBB")
                                .padding()
                        }
                        Button {
                            withAnimation {
                                selectedTab = 2
                                self.proxy?.scrollTo(2, anchor: .center)
                            }
                        } label: {
                            Text("BBBB")
                                .padding()
                        }
                    }
                }
            }
            .background(Color.green)
        }
    
        @ViewBuilder private var tabContentView: some View {
            ScrollViewReader { proxy in
                ScrollView(.horizontal, showsIndicators: false) {
                    HStack(spacing: 0) {
                        ForEach(0..<3, id: .self) { i in
                            DummyScreen(title: "Name: (i)", color: .blue)
                                .frame(idealWidth: UIScreen.main.bounds.width)
                                .id(i)
                        }
                    }
                }
                .onAppear {
                    self.proxy = proxy
                }
            }
        }
    }
    

  2. Just like Asperi said, you can change to another type of View to make the transition works.

    I tried your code, changed List to VStack with ScrollView inside to wrap around the tabContentView, and the result of the UI showed the same except with a proper animation now, and you don’t have to manually adjust the height of your contents since HStack height is dynamic based on your Text() growth.

    Edited: header fixed, animation fixed

    import SwiftUI
    import SwiftUITrackableScrollView //Added
    import Combine
    
    struct GameTabView: View {
    
    @State private var scrollViewContentOffset = CGFloat(0) //Added
    @State var selectedTab: Int = 0
    
    init() {
        UITableView.appearance().sectionHeaderTopPadding = 0
    }
    
    var body: some View {
        listView
            .ignoresSafeArea()
    }
    
    var listView: some View {
        ZStack { //Added
            TrackableScrollView(.vertical, showIndicators: true, contentOffset: $scrollViewContentOffset)  {
                VStack {
                    Color.gray.frame(height: 400)
                    sectionView
                }
            }
            if(scrollViewContentOffset > 400) {
                VStack {
                    headerView
                    Spacer()
                }
            }
        }
    }
    
    var sectionView: some View {
        Section {
            tabContentView
                .transition(.scale) // FIXED
                .background(Color.blue)
        } header: {
            headerView
        }
    }
    
    private var headerView: some View {
        ScrollViewReader { proxy in
            ScrollView(.horizontal, showsIndicators: false) {
                HStack(spacing: 16) {
                    Button {
                        withAnimation {
                            selectedTab = 0
                        }
                    } label: {
                        Text("AAAA")
                            .padding()
                    }
                    Button {
                        withAnimation {
                            selectedTab = 1
                        }
                    } label: {
                        Text("BBBB")
                            .padding()
                    }
                    Button {
                        withAnimation {
                            selectedTab = 2
                        }
                    } label: {
                        Text("BBBB")
                            .padding()
                    }
                }
            }
        }
        .background(Color.green)
    }
    
    @ViewBuilder private var tabContentView: some View {
        switch selectedTab {
        case 0:
            DummyScreen(title: "FIRST", color: .red)
        case 1:
            DummyScreen(title: "SECOND", color: .green)
        case 2:
            DummyScreen(title: "THIRD", color: .blue)
        default:
            EmptyView()
        }
    }
    }
    
    struct DummyScreen: View {
    let title: String
    let color: Color
    var body: some View {
        VStack {
            ForEach(0..<15, id: .self) { index in
                HStack {
                    Text("#(index): title (title)")
                        .foregroundColor(Color.black)
                        .font(.system(size: 30))
                        .padding(.vertical, 20)
                    Spacer()
                }
                .background(Color.yellow)
            }
        }
        .background(color)
    }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search