1

テーブルビューを使用してクエリの結果を表示するviewControllerがあります)。行をタップして、childViewを押し、右側に2つのボタン(前へ/次へ)を含むナビゲーションバーを設定します。私の質問は、前または次のボタンをタップしたときに、前または次の「childView」に切り替えるにはどうすればよいですか?ビューが切り替わるときのトランジション効果も欲しいですか?何か助けはありますか?

4

3 に答える 3

3

キャンプのリストを含むビューがあり、1つに触れると、ユーザーはキャンプの詳細に移動します。ユーザーが左右にスワイプして、それぞれの詳細を表示しているキャンプのリストを移動できるようにしたかったのです。スワイプを表示し、[戻る]ボタンは常にナビゲーションスタックを上に移動する、つまりリストに戻ることを意味するビジュアルアニメーションが必要でした。

結果を含むテーブルを持つ最初のビューは、「前」と「次」の意味を知っている唯一のオブジェクトであるため、スワイプアクションの実装を担当します。そのビューの「現在表示されている」行/セクションに変更を加えるため、ビューは現在のビューではありませんが、それを追跡するために変数/プロパティを追加する必要があります。

ナビゲーションコントローラーはすでにビューの変更をアニメーション化できるため、ほとんどの作業をナビゲーションコントローラーに任せています。「前」に移動するときは、前のエントリの詳細を使用して新しいビューを作成し、それをナビゲーションスタック(LISTビューと現在のDETAILビューの間)に挿入し、視覚効果を提供し、詳細ビューをアンロードするpopViewControllerAnimatedを実行しますアニメーション化されました。

「次の」キャンプの詳細に移動するには、次のエントリの詳細を含む新しいビューを作成し、ナビゲーションスタックの最後に追加してアニメーション化し、アニメーション化したばかりの詳細ビューを削除してナビゲーションスタックをクリーンアップします。オフ。

つまり、簡単に言うと、詳細ビ​​ューはスワイプジェスチャを検出し、親に通知します。親は、表示する次/前の行を決定します。親は現在のビューを新しいビューに置き換え、置き換えを左右にアニメーション化して効果を視覚的に示します。親はまた、ナビゲーションスタックを更新して、[戻る]ボタンが、以前に表示されたDETAILビューではなく、常にLISTビューへのポップとして機能するようにします。

ここにすべてのコードを投稿することはできませんが、以下が大部分です。注意すべきいくつかの特定のGOTCHAS:

  1. アニメーション化中にVCを削除しようとすると、ナビゲーションスタックを操作すると、実行時に警告エラーが発生する可能性があります。したがって、完全に置き換えられるまで待ってから、ナビゲーションコントローラーのデリゲートとして登録し、「didShowViewController」メソッドを使用して安全に変更できるかどうかを検出することにより、削除します。この複雑さは、リストを前に進む場合にのみ必要です。これは、「戻る」ロジックでは、ナビゲーションコントローラーがpopViewControllerの後に自分自身をクリーンアップするためです。

  2. didShowViewControllerを使用するには、デリゲートを設定する必要があります。デリゲートは、消える可能性のあるVCであってはなりません。そうでない場合、クラッシュが発生します。制御リストビュー自体をデリゲートとして設定しているだけです。

  3. ユーザーが詳細を表示している行/セクションを操作すると、非表示のLISTビューでハイライトを移動(およびテーブルをスクロール)して、最終的に「戻る」と、最後に表示されたアイテムが表示されるようにします。 。

DETAILビューを作成するときは、親を渡し、スワイプが発生したことを親に通知するメソッドを定義し、viewDidLoadメソッドでDETAILビューにスワイプ認識機能を登録します。

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を使用してから、右側に次のボタンを追加します。

または、ナビゲーションコントローラのバーを非表示にして、独自の2つのボタンを追加します(現在のアプローチだと思います)。

ナビゲーションコントローラーでビューコントローラーをプッシュ/ポップするには:

次のVCに進む:

[myNavController pushViewController:vc animated:YES];

最後のVCに戻る:

myNavController popViewControllerAnimated:YES];

スタックの3番目のVCにポップバックします。

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

メモリ使用量を最小限に抑えたい場合は、独自のナビゲーションバーと独自のボタンを使用し、仮想スタック内の場所の独自のコード化されたインデックス/カウントを使用して、2つのViewControllerのみを使用できるようにしてください。

于 2011-12-21T17:06:56.847 に答える