【问题标题】:Iphone navigate to previous/next viewControllerIphone 导航到上一个/下一个 viewController
【发布时间】:2011-06-05 18:02:18
【问题描述】:

我有一个 viewController,它使用 tableview 显示查询的结果)。 通过点击一行,我按下 childView 并设置了一个导航栏,其中包含右侧的 2 个按钮(上一个/下一个)。 我的问题是: 当我点击上一个或下一个按钮时,如何切换到上一个或下一个“childView”? 我想在视图切换时也有一个过渡效果? 有什么帮助吗?

【问题讨论】:

    标签: iphone navigation transition switching


    【解决方案1】:

    我有一个包含营地列表的视图,触摸它会将用户带到营地的详细信息。我想让用户左右滑动浏览营地列表,显示每个营地的详细信息。我想要一个显示滑动的视觉动画,并且“返回”按钮总是指向后移动导航堆栈,即返回到列表。

    具有结果表的第一个视图是唯一知道“上一个”和“下一个”含义的对象,因此它们将负责实现滑动操作。因为我们将更改该视图中“当前显示的”行/部分,而该视图不是当前视图,所以您需要添加变量/属性来跟踪它。

    因为导航控制器已经可以为视图更改设置动画,所以我让它完成了大部分工作。当移动“previous”时,我创建了一个包含先前条目详细信息的新视图,将其插入导航堆栈(在 LIST 视图和当前 DETAIL 视图之间),并执行 popViewControllerAnimated 提供视觉效果并卸载详细信息视图只是动画关闭。

    为了移动到“下一个”营地细节,我创建了一个包含下一个条目细节的新视图,将其添加到导航堆栈的末尾,动画进入,然后我通过删除详细信息视图来清理导航堆栈那只是动画。

    所以,简而言之: 详细信息视图将检测滑动手势并通知父级。 父级确定应显示的下一行/上一行。 父级将当前视图替换为新视图,为替换左/右设置动画以直观地指示效果。父级还更新导航堆栈以确保“返回”按钮始终充当 LIST 视图的弹出窗口,而不是之前显示的 DETAIL 视图。

    我不能在这里发布我的所有代码,但下面是大多数代码。需要注意的一些特定问题:

    1. 如果您在 VC 正在制作动画时尝试删除它,则操作导航堆栈可能会导致运行时出现警告错误。因此,我们等到它被完全替换,然后通过注册为导航控制器的委托来移除它,并使用“didShowViewController”方法来检测何时可以安全地进行更改。只有在列表中向前移动时才需要这种复杂性,因为在“后退”逻辑中,导航控制器会在 popViewController 之后自行清理。

    2. 为了使用 didShowViewController,您必须设置一个委托。委托人不能是可能会消失的 VC,否则你会崩溃。我只有将控制 LIST 视图设置为委托。

    3. 当您操作用户正在查看其详细信息的行/部分时,我还会在隐藏的 LIST 视图上移动突出显示(并滚动表格),以便当他们最终“返回”到它时,显示最后查看的项目。

    在创建DETAIL视图时,传入父视图,定义方法让父视图知道发生了滑动,并在DETAIL视图上注册滑动识别器,在viewDidLoad方法中,

    LIST 视图中的代码(父级)

        -(NSString *) nameOfPreviousCampAndUpdateCurrents;
        {
            // pseudo code
            //  targetsection = srcSection
            //  targetrow = srcRow-1.
            // if targetrow < 0
            //   targetsection = srcSection - 1
            //   targetrow = last row of targetsection
            // if targetSection <  0
            //   return nil;
            //
            // return name at targetsection, targetrow
    
            NSInteger targetSection;
            NSInteger targetRow;
            NSString  *results = nil;
    
            targetSection = self.currentDetailViewSection;
            targetRow = self.currentDetailViewRow-1;
    
            if (targetRow < 0)
            {
                targetSection--;
                if (targetSection <0)
                {
                    return nil;
                }// end if
    
                NSInteger numberOfRowsInSection = [self tableView:self.myTable numberOfRowsInSection:targetSection];        
                targetRow = numberOfRowsInSection-1;        
            }// end if
    
            results = [self getCampNameInSection:targetSection atOffset:targetRow];
            self.currentDetailViewSection = targetSection;
            self.currentDetailViewRow = targetRow;
    
            return results;
    
        }
    
       - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
        {
            // Navigation logic may go here. Create and push another view controller.
    
            CampDetails *detailViewController = [[[CampDetails alloc] initWithNibName:nil bundle:nil] autorelease];
            detailViewController.campName = [self getCampNameInSection:indexPath.section atOffset:indexPath.row];
            detailViewController.campID = [self getCampIDForSection:indexPath.section atOffset:indexPath.row];
            detailViewController.parent = self;
    
            self.currentDetailViewSection = indexPath.section;
            self.currentDetailViewRow = indexPath.row;
    
            // ...
            // Pass the selected object to the new view controller.
            [[self navigationController] pushViewController:detailViewController animated:YES];
            //[detailViewController release];
    
        }
    
        -(void) viewDidLoad;
        {
            // The ROOT view controller should do this.
            if ([[self.navigationController viewControllers] count] == 1)
            {
                self.navigationController.delegate = self;        
            }// end if
        }
    
        -(void) moveToNextCamp;
        {
            NSString *nextCamp = [self nameOfNextCampAndUpdateCurrents];
    
            if (nextCamp == nil)
            {
                UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"Warning"
                                                                message:@"You are already at the last item in the list of camps."
                                                               delegate:self
                                                      cancelButtonTitle:@"OK"
                                                      otherButtonTitles:nil];
                [alert show];
                [alert release];
    
                return;
            }// end if
    
            CampDetails *detailViewController = [[[CampDetails alloc] initWithNibName:nil bundle:nil]autorelease];
            detailViewController.campName = nextCamp;
            detailViewController.campID = [self getCampIDForSection:self.currentDetailViewSection atOffset:self.currentDetailViewRow];
            detailViewController.parent = self;
    
            // do the animation to the right
            [self.navigationController pushViewController:detailViewController animated:YES];
    
    
        //    remove the previous controller so that popping the current one takes us "up"
        //    WHILE THE FOLLOWING CODE DOES WORK, it also results in a runtime warning.  
        //    so instead, we tinker with the controller stack only when it's safe (see below)
        //    NSMutableArray *viewControllers = [NSMutableArray arrayWithArray:self.navigationController.viewControllers];
        //    [viewControllers removeObjectAtIndex:1];
        //    [self.navigationController setViewControllers:viewControllers animated:NO];
        //    
    
            // clean up the stack AFTER the child is shown.  
            self.userJustSwiped = YES;
    
            [self updateTableHighlightAndScrollPosition];
    
    
        }
        -(void) moveToPreviousCamp;
        {
            NSString *previousCamp = [self nameOfPreviousCampAndUpdateCurrents];
    
            if (previousCamp == nil)
            {
                UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"Warning"
                                                                message:@"You are already at the first item in the list of camps."
                                                               delegate:self
                                                      cancelButtonTitle:@"OK"
                                                      otherButtonTitles:nil];
                [alert show];
                [alert release];
    
                return;
            }// end if
    
            CampDetails *detailViewController = [[[CampDetails alloc] initWithNibName:nil bundle:nil]autorelease];
            detailViewController.campName = previousCamp;
            detailViewController.campID = [self getCampIDForSection:self.currentDetailViewSection atOffset:self.currentDetailViewRow];
            detailViewController.parent = self;
    
            // add the controller so that popping the current one takes us there    
            NSMutableArray *viewControllers = [NSMutableArray arrayWithArray:self.navigationController.viewControllers];    
            NSInteger lastNavStackEntryIndex =  [viewControllers count]-1;    
            [viewControllers insertObject:detailViewController atIndex:lastNavStackEntryIndex];
            [self.navigationController setViewControllers:viewControllers animated:NO];
    
            // do the animation (which also releases the previously current vc)
            [self.navigationController popViewControllerAnimated:YES];
    
            [self updateTableHighlightAndScrollPosition];
    
        }
    
        - (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
        {
            // IF we just swiped to a details view, do some clean up
            if (self.userJustSwiped)
            {
                self.userJustSwiped = NO;
                // clean up the stack AFTER the child is shown.  remove the previous controller so that popping the current one takes us "up"
                NSMutableArray *viewControllersArray = [NSMutableArray arrayWithArray:navigationController.viewControllers];
    
                NSInteger lastNavStackEntryIndex =  [viewControllersArray count] - 1;
    
                [viewControllersArray removeObjectAtIndex:lastNavStackEntryIndex-1];
                [navigationController setViewControllers:viewControllersArray animated:NO];        
    
            }// end if
    
    
    
        }
    
        -(void) userSwipedLeftOnChild;
        {
            [self moveToNextCamp];
        }
    
        -(void) userSwipedRightOnChild;
        {
            [self moveToPreviousCamp];
        }
    

    DETAILS 视图中的代码(子):

    -(void) leftSwipe:(UIGestureRecognizer*)recognizer;
    {
        [self.parent userSwipedLeftOnChild];
    }
    -(void) rightSwipe:(UIGestureRecognizer*)recognizer;
    {
        [self.parent userSwipedRightOnChild];
    }
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
    
        // add swipe recognizers
        UISwipeGestureRecognizer *leftSwipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(leftSwipe:)];
        [leftSwipe setDirection:UISwipeGestureRecognizerDirectionLeft];
        [self.view addGestureRecognizer:leftSwipe];
        [leftSwipe release];
    
    
        UISwipeGestureRecognizer *rightSwipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(rightSwipe:) ];  
        [rightSwipe setDirection:UISwipeGestureRecognizerDirectionRight];
        [self.view addGestureRecognizer:rightSwipe];
        [rightSwipe release];
    }
    

    【讨论】:

      【解决方案2】:

      点击这些按钮时,您可以轻松推送和弹出带有动画的视图控制器。

      如果您需要帮助,请告诉我。

      【讨论】:

        【解决方案3】:

        使用自动有一个后退按钮的 UINavigationController,然后只需在右侧添加下一个按钮。

        或者只是隐藏导航控制器的栏并添加您自己的两个按钮(我认为您当前的方法)。

        在导航控制器上推送/弹出视图控制器:

        推进到下一个vc:

        [myNavController pushViewController:vc animated:YES];
        

        弹回上一个vc:

        myNavController popViewControllerAnimated:YES];
        

        弹回堆栈上的第三个 vc:

        [myNavController popToViewController:[myNavController.viewControllers objectAtIndex:2] animated:YES];
        

        请注意,如果您想最大限度地减少内存使用量,请使用您自己的导航栏和您自己的按钮,并通过使用您自己在虚拟堆栈中的编码索引/计数来确保您只有两个视图控制器在运行。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-11-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-08-17
          相关资源
          最近更新 更多