1

I have this simple View that takes an array of Views. The idea is that clicking on the visible view slides the next view into place. This is working fine if I index my Views array with constants but does not work if the index is a @State var.

This is what is NOT working:

struct ImageCarousel<Page : View>: View {
    let pages:[Page]
    @State private var currentImage = 0
    init(_ views:[Page]) {
        pages = views
    }
    var body: some View {
        VStack {
            pages[self.currentImage]
                .transition(.slide)
                .animation(.easeInOut)
                .onTapGesture {
                    withAnimation {
                        self.currentImage = self.currentImage+1 >= self.pages.count ? 0 : self.currentImage+1
                    }
            }
        }
    }
}

But this version of body does work but is not useful:

var body: some View {
    VStack {
        if self.currentImage == 0 {
            pages[0]
                .transition(.slide)
                .animation(.easeInOut)
                .onTapGesture {
                    withAnimation {
                        self.currentImage = 1
                    }
            }
        } else {
            pages[1]
                .transition(.slide)
                .animation(.easeInOut)
                .onTapGesture {
                    withAnimation {
                        self.currentImage = 0
                    }
            }
        }
    }
}

If I do not have the IF statement and just use pages[self.currentImage] the transition does not work.

You would use this View this like:

ImageCarousel([
            Rectangle().foregroundColor(.red),
            Rectangle().foregroundColor(.orange),
            Rectangle().foregroundColor(.yellow),
            Rectangle().foregroundColor(.green),
            Rectangle().foregroundColor(.blue),
            Rectangle().foregroundColor(.purple)
        ])

Any insight is greatly appreciated. I feel like this is something obvious that I am not seeing.

pawello2222
  • 46,897
  • 22
  • 145
  • 209
P. Ent
  • 1,654
  • 1
  • 12
  • 22
  • 1
    The `transition` is not appropriate instrument for this scenario, because works in view insert/delete only (as you found in second code snapshot). You need to operate with offset/position and animation. I think [this can be helpful](https://stackoverflow.com/a/58708206/12299030) – Asperi Aug 01 '20 at 03:54
  • I will check that out, but isn't changing the index variable the same as changing the index with a constant? There is something about SwiftUI I am still not grasping. – P. Ent Aug 01 '20 at 13:33

1 Answers1

0

I came up with a solution that isn't too complicated (more of a hack): Temporarily replace the current View with a dummy View and when that appears, trigger the next View to appear. This satisfies the condition of removing a View and replacing it with another View:

var body: some View {
    ZStack {
        if self.currentImage == -1 {
            Rectangle()
                .foregroundColor(.gray)
                .onAppear {
                    self.currentImage = self.nextImage
            }
        } else {
            pages[self.nextImage]
                .transition(.slide)
                .animation(.easeInOut)
                .onTapGesture {
                    withAnimation {
                        self.nextImage = self.nextImage+1 >= self.pages.count ? 0 : self.nextImage+1
                        self.currentImage = -1
                    }
            }
        }
    }
}

This works fine for this simplified case. I did not really want to go into UIScrollView or UIPageViewController territory if I didn't have to.

P. Ent
  • 1,654
  • 1
  • 12
  • 22