Swipe left or right to load the view controller wi

2019-01-15 17:47发布

问题:

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 my firstvc wil disply in my sub view on my mainviewcontroller. Its same for 2, 3rd cell too. Its fine .

Now i need one functionality like, if i swipe my subview which will have all my view controllers when i am selecting any cell. I need to add swipe left or right.

For example :

if i am in firstvc then my first cell will be selected . Now if i swipe my subview to right my secondvc have to come mean while my second cell of collection view also have to highlight

I dont know how to applay.

my code :

  @IBOutlet weak var collectionview: UICollectionView!

    @IBOutlet weak var contentSubView: UIView!
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return self.items.count
    }

    // make a cell for each cell index path
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

        // get a reference to our storyboard cell
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath as IndexPath) as! MyCollectionViewCell

        // Use the outlet in our custom class to get a reference to the UILabel in the cell
        cell.myLabel.text = self.items[indexPath.item]
       // cell.backgroundColor = UIColor.cyan // make cell more visible in our example project

        var borderColor: UIColor! = UIColor.clear
        //var borderWidth: CGFloat = 0

        if indexPath == selectedIndexPath{
            borderColor = UIColor.red
            //borderWidth = 1 //or whatever you please
        }else{
            borderColor = UIColor.white
           // borderWidth = 0
        }

        cell.myview.backgroundColor = borderColor
       // cell.myview.layer.borderWidth = borderColor

        return cell
    }


    // MARK: - UICollectionViewDelegate protocol

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        print("You selected cell #\(indexPath.item)!")

        //based on your comment remove the subviews before add on your myview
        //let cell = collectionView.cellForItemAtIndexPath(indexPath) as! MyCollectionViewCell

        selectedIndexPath = indexPath

        for subview in contentSubView.subviews {
            subview.removeFromSuperview()
        }

        let alertStoryBoard =  UIStoryboard(name: "Main", bundle: nil)
        var controller: UIViewController!

        if indexPath.item == 0 {

            if  let allCollectionViewController = alertStoryBoard.instantiateViewController(withIdentifier:"firstvc") as? firstvc  {

                controller = allCollectionViewController
            }

        } else if indexPath.item == 1 {

            if  let allCollec = alertStoryBoard.instantiateViewController(withIdentifier:"secondvc") as? secondvc  {
                controller = allCollec
            }


        }else if indexPath.item == 2 {


            if  let wController = alertStoryBoard.instantiateViewController(withIdentifier:"Thirdvc") as? Thirdvc  {
                controller = wController
            }

        }

        addChildViewController(controller)

        // Add the child's View as a subview
        contentSubView.addSubview(controller.view)
        controller.view.frame = contentSubView.bounds
        controller.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]

        // tell the childviewcontroller it's contained in it's parent
        controller.didMove(toParentViewController: self)

}

My full project zip : https://www.dropbox.com/s/fwj745kdgqvgjxa/Collection%20view%20example.zip?dl=0

回答1:

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:).