Swipe left or right to load the view controller with the collection view cell highlight

后端 未结 1 977
伪装坚强ぢ
伪装坚强ぢ 2020-12-06 15:41

I have 3 view controllers called firstvc, secondvc, thirdvc. And I have one collection view which will scroll horizontally. If i select my first cell

相关标签:
1条回答
  • 2020-12-06 16:08

    You should just add UISwipeGestureRecognizer to the main view controller's view. The main view controller would be responsible for managing the gesture's calls

    Basically in code:

    in viewDidLoad:

    let swipeToLeft = UISwipeGestureRecognizer(target: self, action: #selector(changePageOnSwipe(_:)))
    let swipeToRight = UISwipeGestureRecognizer(target: self, action: #selector(changePageOnSwipe(_:)))
    swipeToLeft.direction = .right 
    swipeToRight.direction = .left
    self.contentSubView.addGestureRecognizer(swipeToLeft) // Gesture are added to the top view that should handle them
    self.contentSubView.addGestureRecognizer(swipeToRight)
    

    Since you will have to move from a VC at an index to another index you might need a property for keeping track of the currently selected view controller:

    var currentIndexPath: IndexPath?
    

    And you can change the value of it each time a new VC is selected. So:

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        print("You selected cell #\(indexPath.item)!")
        self.currentIndexPath = indexPath
        // ... Other settings
    }
    

    add the changePageOnSwipe(_ gesture: UISwipeGestureRecognizer) method in your main ViewController. Since it is the "chief" view controller who possesses the collectionView, it will handle the swipes and tell its children to appear:

    func changePageOnSwipe(_ gesture: UISwipeGestureRecognizer) {
        guard let indexPath = self.currentIndexPath else {
            // When the page loads and there is no current selected VC, the swipe will not work
            // unless you set an initial value for self.currentIndexPath
            return
        }
    
        var newIndex = indexPath    // if self.collectionview.indexPathsForSelectedItems is not empty, you can also use it instead of having a self.currentIndexPath property 
    
        // move in the opposite direction for the movement to be intuitive
        // So swiping " <-- " should show index on the right (row + 1)
        if gesture.direction == .left {
            newIndex = IndexPath(row: newIndex.row+1, section: newIndex.section)
        } else {
            newIndex = IndexPath(row: newIndex.row-1, section: self.currentIndexPath!.section)
        }
    
        if canPresentPageAt(indexPath: newIndex) {
            // Programmatically select the item and make the collectionView scroll to the correct number
            self.collectionview.selectItem(at: newIndex, animated: true, scrollPosition: UICollectionViewScrollPosition.centeredHorizontally)
            // The delegate method is not automatically called when you select programmatically
            self.collectionView(self.collectionview, didSelectItemAt: newIndex!)
        } else {
            // Do something when the landing page is invalid (like if the swipe would got to page at index -1 ...)
            // You could choose to direct the user to the opposite side of the collection view (like the VC at index self.items.count-1
            print("You are tying to navigate to an invalid page")
        }
    }
    

    and since you are doing the swipe programmatically, you have to make sure that the swipe is valid before trying to actually move. You have to add safety checks:

    /** You can use an helper method for those checks
     */
    func canPresentPageAt(indexPath: IndexPath) -> Bool {
        // Do necessary checks to ensure that the user can indeed go to the desired page
        // Like: check if you have a non-nil ViewController at the given index. (If you haven't implemented index 3,4,5,... it should return false)
        //
        // This method can be called either from a swipe
        // or another way when you need it
        if indexPath.row < 0 || indexPath.row >= self.items.count {
            print("You are trying to go to a non existing page")
            return false
        } else {
            print("If you haven't implemented a VC for page 4 it will crash here")
            return true;
        }
    }
    

    Finally, you can set a default indexPath for self.currentIndexPath in viewDidLoad so that the user can already swipe when he lands on your main VC without having selected another VC in the collectionView.


    Note: If you happen to have gesture recognisers in the sub-ViewControllers, some gestures may conflict and you would have to learn how to resolve such conflicts with delegate methods such as gestureRecognizer(_:shouldRequireFailureOf:).

    0 讨论(0)
提交回复
热议问题