Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Functionality similar to scrollToItem(at: indexPath) from UICollectionView #23

Closed
marcmora opened this issue Nov 15, 2018 · 13 comments
Closed

Comments

@marcmora
Copy link

marcmora commented Nov 15, 2018

Is there a way i could skip straight to whatever card of the stack? So the user can go back and forth starting from that card.

Thanks!

@JoniVR
Copy link
Owner

JoniVR commented Nov 15, 2018

Hi @marcmora

Not officially supported yet, but since the VerticalCardSwiper is built with a UICollectionView, you can still do it pretty easily.

Here's how it should work:

let indexToScrollTo = 9
verticalCardSwiperView.scrollToItem(at: IndexPath(row: indexToScrollTo, section: 0), at: .top, animated: true)

You can access the verticalCardSwiperView in the appropriate delegate function you're using or just by accessing it through verticalCardSwiper.verticalCardSwiperView.

Hope this helps

Kind regards,
Joni

JoniVR pushed a commit that referenced this issue Nov 16, 2018
…iper

- add ScrollToCard function
- move indexesForVisibleCards to VerticalCardSwiper
- update documentation
@JoniVR
Copy link
Owner

JoniVR commented Nov 16, 2018

I have added this function to the VerticalCardSwiper in the development branch, will be released with the next version. I'm closing this issue for now, feel free to reopen.

@JoniVR JoniVR closed this as completed Nov 16, 2018
@marcmora
Copy link
Author

Hi @JoniVR thanks for the quick reply and for exposing the function!

I did try to call the scrollToItem method from UICollectionView before raising the the question but for some reason it didn't work.
Playing around, I've discovered that if call cardSwiper.scrollToCard(at: 3, animated: true) from a button it works like a charm!
But if I add it in the viewDidLoad() method, like I did in the first place, it does not.

Am I missing something? I'm adding it right after registering the Nib as I want the card number n to be the starting point when the user lands into this view controller.

@JoniVR
Copy link
Owner

JoniVR commented Nov 16, 2018

I'll look into it when I get some more time, thanks for bringing it to my attention. 🙂

@JoniVR
Copy link
Owner

JoniVR commented Nov 18, 2018

Okay, so here's probably what's causing your issue:

When you call the scrollToCard function inside viewDidLoad, the underlying UICollectionView isn't fully layed out yet. So it won't work that way (this is would be the same when you'd be using a UICollectionView), so instead, you should call this function inside viewDidLayoutSubviews.

It would look like this in your UIViewController:

override func viewDidLayoutSubviews() {
    cardSwiper.scrollToCard(at: 5, animated: true)
}

This should work (I've tried it in the example), however, if it doesn't, feel free to let me know 🙂

I'll update the documentation on this function to indicate that for future users.

Have a nice day 🙂

edit:
See: https://stackoverflow.com/a/42964452/6863743

JoniVR pushed a commit that referenced this issue Nov 18, 2018
@Phuzer
Copy link

Phuzer commented Nov 20, 2018

Hello, i'm experiencing a problem with scrollToCard function. For some reason I can't use it to go to the previous card. For example, if the card with index 4 is active, cardSwiper.scrollToCard(at: 3, animated: true) does nothing, but I can use it to move to any other card (index 0, 1, 2, 5 and so on...). Any idea on how to solve this? Thanks in advance.

@JoniVR
Copy link
Owner

JoniVR commented Nov 21, 2018

Hi @Phuzer
I'm seeing the same thing. Thanks for letting me know.
I'll look into if further whenever I get some time.

@marcmora Does that solve your specific issue?

@JoniVR
Copy link
Owner

JoniVR commented Nov 21, 2018

@Phuzer Ok, so after looking into this a bit further, I think it's a bug that's related to the updateCellAttributes function inside VerticalCardSwiperFlowLayout.
It only happens when trying to scroll to the previous card.
I'll look into fixing it inside that function, might take some time as I'm currently pretty busy with other stuff.

@pcentieiro
Copy link

pcentieiro commented Nov 22, 2018

Hello @JoniVR

I need the option to programatically go to the previous/next card, and as you're already aware, there is a bug in scrollToCard. Therefore, I implemented two methods in ExampleViewController based on scrollRectToVisible:

func goToNext() {
   let index = cardSwiper.indexesForVisibleCards.last!
   let height = cardSwiper.verticalCardSwiperView.layoutAttributesForItem(at: IndexPath(item: index, section: 0))!.frame.height
   var indexOffset = 2
   
   // Top of stack (one card visible)
   if (cardSwiper.indexesForVisibleCards.count == 1) {
       indexOffset = 1
   }

   let rect = CGRect(x: 0, y: (height + cardSwiper.cardSpacing) * CGFloat(index - indexOffset), width: cardSwiper.verticalCardSwiperView.frame.width - cardSwiper.sideInset * 2, height: height)
   cardSwiper.verticalCardSwiperView.scrollRectToVisible(rect, animated: true)
}
func goToPrevious() {
    let index = cardSwiper.indexesForVisibleCards.last!
    let height = cardSwiper.verticalCardSwiperView.layoutAttributesForItem(at: IndexPath(item: index, section: 0))!.frame.height
            
    let rect = CGRect(x: 0, y: (height + cardSwiper.cardSpacing) * CGFloat(index) - cardSwiper.cardSpacing, width: cardSwiper.verticalCardSwiperView.frame.width - cardSwiper.sideInset * 2, height: height)
        
    cardSwiper.verticalCardSwiperView.scrollRectToVisible(rect, animated: true)
}

It's a bit fishy, but it works :) I hope it can inspire you to come up with a better solution in VerticalCardSwiper.swift

Thanks for the project ;) Keep up the good work!

PS: In our app we start with all the cards loaded on the stack hence the names goToNext()/goToPrevious() and their behaviours.

@JoniVR
Copy link
Owner

JoniVR commented Nov 22, 2018

@pcentieiro Thanks for posting this!

I've also looked into using the scrollRectToVisible method before, it's a great solution for people that want to use it right now.
I'm going to try to fix it in the updateCellAttributes function, the line that's causing it is let finalY = max(minY, maxY), so I'm going to try to properly fix it since #22 is related to that function too. Shame that I'm a bit constrained on free time right now.. again, thanks a lot for posting this, I'm sure it can help some people in the meantime 😄

@JoniVR
Copy link
Owner

JoniVR commented Nov 24, 2018

Ok, so little update, I think I've figured out what the problem is, I commited some updated documentation on updateCellAttributes yesterday, which explains how the function works.
Here it is:

Below we'll briefly explain how the effect of scrolling a card to the background instead of the top is achieved.
Keep in mind that (x,y) coords in views start from the top left (x: 0, y: 0) and increase as you go down/to the right, so as you go down, the y-value increases, and as you go right, the x value increases.

The two most important variables we use to achieve this effect are cvMinY and cardMinY.

  • cvMinY: The top position of the collectionView + inset. On the drawings below it's marked as "A".
    This position never changes (the value of the variable does, but the position is always at the top where "A" is marked).
  • cardMinY: The top position of each card. On the drawings below it's marked as "B". As the user scrolls a card,
    this position changes with the card position (as it's the top of the card).
    When the card is moving down, this will go up, when the card is moving up, this will go down.

We then take the max(cvMinY, cardMinY) to get the highest value of those two and set that as the origin.y of the card.
By doing this, we ensure that the origin.y of a card never goes below cvMinY, thus preventing cards from scrolling upwards.

 +---------+   +---------+
 |         |   |         |
 | +-A=B-+ |   |  +-A-+  | ---> The top line here is the previous card
 | |     | |   | +--B--+ |      that's visible when the user starts scrolling.
 | |     | |   | |     | |
 | |     | |   | |     | |  |  As the card moves down,
 | |     | |   | |     | |  v  cardMinY ("B") goes up.
 | +-----+ |   | |     | |
 |         |   | +-----+ |
 | +--B--+ |   | +--B--+ |
 | |     | |   | |     | |
 +-+-----+-+   +-+-----+-+

Essentially, what's happening is that when try to scroll to the previous card, the updateCellAttributes function executes max(cvMinY, cardMinY) on both visible cards (focussed and bottom card). In case of the bottom card, when it tries to move down (because we we want to scroll to the previous card), the card actually moves up instead of down (because its cardMinY is below cvMinY and we take the highest value of those two, causing the card to move up instead). This would explain why this only happens when trying to scroll to the previous card.

I hope this explains it well enough to understand it.

at first I was hoping simply doing something like:

if let frame = verticalCardSwiperView.layoutAttributesForItem(at: convertIndexToIndexPath(for: index))?.frame {
    verticalCardSwiperView.scrollRectToVisible(frame, animated: true)
}

would fix it, but it gives the exact same result.
I’m still thinking about a good solution for this, but at least now we know what’s going wrong.

edit:
Reasons that I'm posting this here are:
A.) To keep everyone up-to-date.
B.) I'm using this thread for myself as a way to document the issue, so I can pickup where I left off next time I look into it.

Also, if you have a fix, suggestion or feedback, feel free to let met know 🙂

@pcentieiro
Copy link

Great explanation @JoniVR , thanks :)

The problem lies in the frame generated that is used on scrollToRectToVisible. Here is the one I get when I use index = 0 and I'm at the second card (so only 2 cards in the stack and I want to go back):

   origin : (17.673921267470945, 523.8456659619451)
    - x : 17.673921267470945
    - y : 523.8456659619451
   size : (299.65215746505805, 473.0)
    - width : 299.65215746505805
    - height : 473.0

And here is the frame that I generate with the code that I posted before (which scrolls to the previous card with no problem):

   origin : (0.0, 0.0)
    - x : 0.0
    - y : 0.0
   size : (335.0, 473.0)
    - width : 335.0
    - height : 473.0

As you can see the problem is on the y value. I'll try to see what this is happening, but I think that you'll get there before me :)

JoniVR pushed a commit that referenced this issue Jan 8, 2019
scrollToItem & scrollRectToVisible were giving issues with reliable scrolling, so we're using setContentOffset for the time being.
@JoniVR
Copy link
Owner

JoniVR commented Jan 8, 2019

I think I've managed to fix it by using setContentOffset() and calculating the offset manually. I can't figure out what exactly is causing the issue with scrollToItem & scrollRectToVisible, but at this point I've wasted enough time trying to find the issue and this works just as well as far as I can tell.

@JoniVR JoniVR closed this as completed Jan 8, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants