Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Control swipe back action in UINavigationController

I'm creating a simple flash card app as illustrated below:

flash cards not animated

I want a swipe backwards to occur like this:

flash cards animated

To do this, onBack(index: Int) is what I need to be called when the swipe back happens (in order to update the card shown):

import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var flashCardLabel: UILabel!

    // Populate initial content
    let content = ["Lorem", "Ipsum", "Dolor", "Sit"]

    // Index of where we are in content
    var index = 0

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    // Label text based on index
    func setLabelToIndex() {
        flashCardLabel.text = content[index]
    }

    // Go back
    @IBAction func back(_ sender: Any) {
        if index > 0 {
            index = index - 1
            setLabelToIndex()
        }
    }

    // Go forward
    @IBAction func next(_ sender: Any) {
        if index + 1 < content.count {
            index = index + 1
            setLabelToIndex()
        }
    }

    // Desired function to be called
    // when swiping back in navigation stack
    func onBack(index: Int) {
        self.index = index
        setLabelToIndex()
    }
}
like image 998
user2560886 Avatar asked Jan 20 '17 00:01

user2560886


3 Answers

If I understand your question correctly, you want to be able to swipe between questions and/or have a swipe effect when you click "Next" or "Back". If that's the case, I suggest you embed your UILabel in a UIScrollView. Check this out:

class ViewController: UIViewController,UIScrollViewDelegate {

    let content = ["Lorem", "Ipsum", "Dolor", "Sit"]
    let scrollView = UIScrollView(frame: CGRect(x: 0, y: 0, width: 320, height: 300))

    var index = 0

    override func viewDidLoad() {
        super.viewDidLoad()

        scrollView.delegate = self        
        scrollView.contentSize = CGSize(width: self.view.frame.width * content.count, height: self.scrollView.frame.size.height)
        scrollView.isPagingEnabled = true

        // add labels to pages
        for i in 0 ..< content.count {
            let label = UILabel(frame: CGRect(x: self.view.center.x * (i + 1), y: self.view.center.y, width: 100, height: 50))
            label.textAlignment = .center
            label.text = content[i]
            scrollView.addSubview(label)
        }
        self.view.addSubview(scrollView)

    }

    // Go back
    @IBAction func back(_ sender: Any) {
        if index > 0 {
            index = index - 1

            // scroll to page
            let offset = CGPoint(x: CGFloat(index) * self.view.frame.width, y: 0)
            self.scrollView.setContentOffset(offset, animated: true)
        }
    }

    // Go forward
    @IBAction func next(_ sender: Any) {
        if index + 1 < content.count {
            index = index + 1

            // scroll to page
            let offset = CGPoint(x: CGFloat(index) * self.view.frame.width, y: 0)
            self.scrollView.setContentOffset(offset, animated: true)
        }
    }

    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        index = round(scrollView.contentOffset.x / scrollView.frame.size.width)
    }
}

Explanation:

You basically create a UIScrollView to handle the pagination effect and add a UILabel to each page with the respective text from the content array. Every time the user scrolls to a different page, index gets updated to the index of the current page. And finally, when the user clicks "Next" or "Back", you scroll over to the next or previous page

like image 191
ThunderStruct Avatar answered Oct 11 '22 15:10

ThunderStruct


If you want it to by Push & Pop navigationcontroller, you can do it by making static index variable. Here is code

class ViewController: UIViewController {


    @IBOutlet weak var flashCardLabel: UILabel!

    // Populate initial content
    let content = ["Lorem", "Ipsum", "Dolor", "Sit"]

    // Index of where we are in content
    static var INDEX = 0;

    override func viewDidLoad() {
        super.viewDidLoad()
        self.next(nil);
    }


    // Label text based on index
    func setLabelToIndex() {
        flashCardLabel.text = content[ViewController.INDEX]
    }

    // Go back
    @IBAction func back(_ sender: Any?) {
        if ViewController.INDEX > 0 {
            ViewController.INDEX = ViewController.INDEX - 1
            setLabelToIndex()
        }
    }

    // Go forward
    @IBAction func next(_ sender: Any?) {
        if ViewController.INDEX + 1 < content.count {
            ViewController.INDEX = ViewController.INDEX + 1
            setLabelToIndex()
        }
    }

    // Desired function to be called
    // when swiping back in navigation stack
    func onBack(index: Int) {
        ViewController.INDEX  -= 1;
        //setLabelToIndex()
    }


    override func didMove(toParentViewController parent: UIViewController?) {
        if parent == nil {
            self.onBack(index: ViewController.INDEX);
        }

    }

}
like image 25
Kiran Patel Avatar answered Oct 11 '22 13:10

Kiran Patel


i can tell you that swipe that UINavigationController suppport is the the swipe when user start swipping his finger from the left of the screen to right just to pop the view from navigation you can not push it back by swipping from right edge to left in iPhone, this is default in UINavigationController

i am writing my code as i am using you need to customize it accordinly, i didn't had time in office to edit, i will tell you more

#pragma mark for pageView
- (UIViewController *) viewControllerAtIndex:(NSUInteger)index
{
if (index > (self.imageArray.count-1))
    return nil;

UIViewController *viewController = nil;    ////
GalleryItems *item = self.imageArray[index];
NSString *cachedGalleryItemName = [item getCachedPhotoFileNameWithPath];
if ([[NSFileManager defaultManager] fileExistsAtPath:cachedGalleryItemName])
{
    ImageViewController *imageVC = [[ImageViewController alloc] initWithNibName:@"ImageViewController" bundle:nil];
    imageVC.galleryItem = item;
    imageVC.cachedGalleryItemName = cachedGalleryItemName;
    imageVC.index = index;
    viewController = imageVC;
}
else
{
    if (self.downloadViewController)
    {
        if (self.indexOfDownloadInProgress == index)
            viewController = self.downloadViewController;
        else
        {
            FileDownloader *fileDownloader = [DataDownloadManager existingFileDownloader:cachedGalleryItemName];
            if (! fileDownloader)
            {
                fileDownloader = [[FileDownloader alloc] init];
                [fileDownloader loadURL:item.photoURL forFilePath:cachedGalleryItemName withReceipt:nil];
                fileDownloader.delegate = nil;
                fileDownloader.notificationName = item.contentId;
                fileDownloader.queuePriority = NSOperationQueuePriorityNormal;
                [[DataDownloadManager sharedInstance].operationQueue addOperation:fileDownloader];
            }
        }
    }
    else
    {
        DownloadViewController *downloadVC = [[DownloadViewController alloc] initWithNibName:@"DownloadViewController" bundle:nil];
        downloadVC.delegate = self;
        downloadVC.downloadCompleteNotificationName = item.contentId;
        downloadVC.asset = item;
        downloadVC.backgroundImageFileName = nil;
        downloadVC.totalFileSize = nil;
        downloadVC.URLString = item.photoURL;
        downloadVC.cachedFileName = cachedGalleryItemName;
        self.indexOfDownloadInProgress = index;
        self.downloadViewController = downloadVC;
        viewController = downloadVC;
    }
}
return viewController;
}

Now use this function to identify the view controller

-(NSUInteger) indexOfViewController:(UIViewController *)viewController
{
NSUInteger index = nil;

if ([viewController isMemberOfClass:[ImageViewController class]])
{
    ImageViewController *currentViewController = (ImageViewController *)viewController;
    index = currentViewController.index;
}
else if ([viewController isMemberOfClass:[DownloadViewController class]])
    index = self.indexOfDownloadInProgress;

return index;
}

- (UIViewController *)viewController:(UIViewController *)viewController ForBeforeAfter:(NSInteger) beforeAfter
{
NSUInteger index = [self indexOfViewController:viewController];

if (index == NSNotFound)
    return nil;

index = index + beforeAfter;
if ([DataDownloadManager sharedInstance].internetNotAvailable)
{
    while (index < self.imageArray.count - 1)
    {
        GalleryItems *item = self.imageArray[index];
        if ([item isDownloaded])
            break;
        index = index + beforeAfter;
    }
}
return [self viewControllerAtIndex:index];
}

now do this in page view controller delegate

-(UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
return [self viewController:viewController ForBeforeAfter:-1];
}

-(UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
return [self viewController:viewController ForBeforeAfter:+1];
}

init page view controller like this

- (void)initPageViewController:(UIViewController *)initViewController
{
self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];

[self.pageViewController setDataSource:self];

[self.pageViewController setViewControllers:@[initViewController] direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil];

[self.pageViewController.view setFrame:self.view.frame];
[self addChildViewController:self.pageViewController];
[self.view addSubview:self.pageViewController.view];
[self.pageViewController didMoveToParentViewController:self];
[self.view sendSubviewToBack:self.pageViewController.view];
}

in viewDidLoad of the class(in my case it is DisplayImageViewController) you are using this page you can add this tine of code for initialization

[self initPageViewController:[self viewControllerAtIndex:self.index]];

this DisplayImageViewController class is used to display the image you just remove the UIIMAGE to something you want.

and before you push this view controller in navigation set the property like this

DisplayImageViewController *divc = initialize display view controller class; // here you just set the item in array in which you want to implement swipe
    divc.imageArray = self.imageArray;
    divc.galleryAsset = self.gallery;
    divc.index = indexPath.item;
    [self presentViewController:divc animated:YES completion:nil];
like image 36
Anurag Soni Avatar answered Oct 11 '22 13:10

Anurag Soni