1

I have a viewController that displays the result of a query using a tableview). By taping on a row a I push a the childView and I set a navigationBar that contains 2 buttons on the right (Previous/Next). My question is : How can I switch to the previous or next "childView" when I tap on previous or next button? I would like to have also a transition effect while the view is switching? Any help?

4

3 回答 3

3

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

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

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

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

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

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

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

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

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

创建 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];
}
于 2012-07-23T17:16:43.207 回答
1

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

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

于 2011-06-05T18:48:40.850 回答
1

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

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

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

推进下一个vc:

[myNavController pushViewController:vc animated:YES];

弹回最后一个vc:

myNavController popViewControllerAnimated:YES];

弹回堆栈上的第 3 个 vc:

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

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

于 2011-12-21T17:06:56.847 回答