user17967018
user17967018

Reputation:

Automatic slide switching (SwiftUI)

I can not understand why the timer does not work, and the text does not scroll automatically.

I tried to do so:

ForEach(0..<numberText)

But I got such an error:

Referencing initializer 'init(_:content:)' on 'ForEach' requires that 'some View' conform to 'TableRowContent'

Full code:

let numberText = ["text1","text2","text3","text4"]

struct TextNew: View {
    
    private let timer = Timer.publish(every: 3, on: .main, in: .common).autoconnect()
    @State private var index = 0
    
    let textdata = TextData.getAllText()
    
    var body: some View {
        
        GeometryReader { proxy in
            
            TabView(selection: $index) {
                
                ForEach(numberText, id: \.self) { num in
                    
                    Text("\(num)")
                        .font(.system(size: 10))
                        .foregroundColor(.white)
                        .tag(num)
                        .padding(.bottom, 50)
                    
                }
            }
            .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
            .frame(height: 80)
            .onReceive(timer, perform: { _ in
                withAnimation {
                    index = index < numberText.count ? index + 1 : 0
                    
                }
            })
            
        }
    }
}

Thanks for any help, I'm new

Upvotes: 3

Views: 2359

Answers (2)

Nasib
Nasib

Reputation: 1579

Improved version of @burnsi 's answer.

//Properties

    @StateObject var vm = HomeVVM()
    private let timer = Timer.publish(every: 5, on: .main, in: .common).autoconnect()
    @State private var index = 1
    @State private var selectedNum: String = ""


//Tabview


TabView(selection: $selectedNum) {
  ForEach(vm.trendingItems) { item in
    HomeSliderItem(image: item.getImage(), text: item.getName()).tag(item.id.uuidString)
  }
}
.tabViewStyle(.page)
.onChange(
  of: selectedNum,
  { oldValue, newValue in
    let newIndex = vm.trendingItems.firstIndex(where: { it in
      it.id.uuidString == selectedNum
    })
    index = newIndex != nil ? newIndex! : 1
  }
)
.onReceive(
  timer,
  perform: { _ in
    withAnimation {
      index = index < vm.trendingItems.count ? index + 1 : 1
      let newCandidate = index - 1
      if newCandidate > -1 && newCandidate < vm.trendingItems.count {
        selectedNum = vm.trendingItems[newCandidate].id.uuidString
      }
    }
  })

✅ It makes sure your app doesn't crash due to an ArrayOutOfBounds exception
✅ Synchronized with the user's scrolling of the TabView

Upvotes: 0

burnsi
burnsi

Reputation: 7754

Try this:

struct TextNew: View {
    
    private let timer = Timer.publish(every: 3, on: .main, in: .common).autoconnect()
    @State private var index = 1
    @State private var selectedNum: String = ""
    
    var body: some View {
        
        GeometryReader { proxy in
            
            TabView(selection: $selectedNum) {
                
                ForEach(numberText, id: \.self) { num in
                    Text("\(num)")
                        .font(.system(size: 10))
                        .foregroundColor(.white)
                        .padding(.bottom, 50)
                }
            }
            .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
            
            .onReceive(timer, perform: { _ in
                withAnimation {
                    index = index < numberText.count ? index + 1 : 1
                    selectedNum = numberText[index - 1]
                }
            })
            
        }
    }
}

Explanation:

The reason for this not working is the type mismatch between your .tag and the $index in your TabView declaration. These have to match. By the way you do not need the .tag here as you are setting it to .self in your ForEach loop.

Upvotes: 5

Related Questions