Iphone перейти к предыдущему / следующему viewController - PullRequest
1 голос
/ 05 июня 2011

У меня есть viewController, который отображает результат запроса с использованием таблицы).При нажатии на строку a я нажимаю childView и устанавливаю панель навигации, которая содержит 2 кнопки справа (Предыдущая / Следующая).Мой вопрос: как я могу переключиться на предыдущий или следующий «childView», когда я нажимаю на предыдущую или следующую кнопку?Я хотел бы также иметь эффект перехода при переключении вида?Любая помощь?

Ответы [ 3 ]

3 голосов
/ 23 июля 2012

У меня был вид, который содержал список лагерей, и касание одного приводит пользователей к деталям лагеря. Я хотел позволить пользователю проводить пальцем влево и вправо, чтобы перемещаться по списку лагерей с отображением деталей каждого из них. Мне нужна была визуальная анимация, которая показывала пролистывание и чтобы кнопка «Назад» всегда означала перемещение назад вверх по стеку навигации, то есть возврат к списку.

Первое представление с таблицей результатов - это единственный объект, который знает, что означают слова «предыдущий» и «следующий», поэтому они будут нести ответственность за реализацию действия смахивания. Поскольку мы будем вносить изменения в «отображаемую в данный момент» строку / раздел в этом представлении, в то время как представление не является текущим представлением, вам потребуется добавить переменные / свойства для отслеживания этого.

Поскольку навигационный контроллер уже может анимировать просмотр изменений, я позволил ему выполнить большую часть работы. При перемещении «предыдущий» я создаю новое представление с предыдущими сведениями о записи, вставляю его в стек навигации (между представлением LIST и текущим представлением DETAIL) и выполняю popViewControllerAnimated, который обеспечивает визуальный эффект, а также выгружает представление сведений. просто оживил.

Чтобы перейти к «следующим» сведениям о лагере, я создаю новое представление со следующими сведениями о записи, добавляю его в конец стека навигации, он оживляет, а затем очищаю стек навигации, удаляя представление сведений. это просто оживило.

Итак, в двух словах: В подробном представлении будут обнаружены жесты смахивания и сообщен родитель. Родитель определяет следующую / предыдущую строку, которая должна отображаться. Родитель заменяет текущий вид новым, анимируя замену влево / вправо, чтобы визуально указать эффект. Родитель также обновляет стек навигации, чтобы кнопка «Назад» всегда действовала как всплывающее окно в представлении LIST, а не в ранее показанном представлении DETAIL.

Я не могу опубликовать весь свой код здесь, но ниже приведено большинство. Некоторые конкретные GOTCHAS, которые нужно остерегаться:

  1. Управление стеком навигации может привести к ошибкам предупреждения во время выполнения, если вы попытаетесь удалить VC во время его анимации. Поэтому мы ждем, пока он не будет полностью заменен, затем удаляем его, зарегистрировавшись в качестве делегата для контроллера навигации и используя метод didShowViewController, чтобы определить, когда безопасно вносить изменения. Это усложнение необходимо только при движении вперед в списке, поскольку в логике «назад» контроллер навигации очищает себя после popViewController.

  2. Чтобы использовать didShowViewController, вы должны установить делегата. Делегат не должен быть VC, который может уйти, иначе вы получите сбой. У меня только управляющее представление LIST, установленное в качестве делегата.

  3. По мере того, как вы управляете строкой / разделом, информацию о которых просматривает пользователь, я также перемещаю выделение (и прокручиваю таблицу) в скрытом представлении LIST, чтобы, когда они в конечном итоге переходили к «BACK», отображается последний просмотренный элемент.

При создании представления 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];
    }

Код в представлении ДЕТАЛИ (дочерний):

-(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];
}
1 голос
/ 21 декабря 2011

Используйте UINavigationController, который автоматически имеет кнопку возврата, а затем просто добавьте следующую кнопку справа.

Или просто скройте панель навигационного контроллера и добавьте две свои собственные кнопки (я думаю, что ваш текущий подход).

Чтобы переместить / выдвинуть viewcontroller на контроллере навигации:

pushвперед к следующему vc:

[myNavController pushViewController:vc animated:YES];

возврат к последнему vc:

myNavController popViewControllerAnimated:YES];

возврат к 3-му vc в стеке:

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

Обратите внимание, еслиВы хотите минимизировать использование памяти, использовать свою собственную панель навигации и свои собственные кнопки и убедиться, что у вас когда-либо есть только два контроллера представления в игре, используя свой собственный закодированный индекс / счет того, где вы находитесь в виртуальном стеке.

1 голос
/ 05 июня 2011

При нажатии этих кнопок можно легко нажимать и выдвигать контроллеры с анимацией.

Если вам нужна помощь, сообщите мне.

...