Cố gắng xử lý hành động của nút điều hướng “quay lại” trong iOS


76

Tôi cần phát hiện thời điểm người dùng nhấn vào nút "quay lại" trên thanh điều hướng để thực hiện một số thao tác khi điều đó xảy ra. Tôi đang cố gắng đặt thủ công một hành động cho nút như vậy, theo cách này:

[self.navigationItem.backBarButtonItem setAction:@selector(performBackNavigation:)];

- (void)performBackNavigation:(id)sender
{
   // Do operations

   [self.navigationController popViewControllerAnimated:NO];
}

Trước tiên, tôi đã đặt mã đó vào chính bộ điều khiển chế độ xem, nhưng tôi nhận thấy điều đó self.navigationItem.backBarButtonItemdường như đúng nil, vì vậy tôi đã chuyển cùng mã đó sang bộ điều khiển chế độ xem mẹ, điều này sẽ đẩy mã trước đó vào ngăn xếp điều hướng. Nhưng tôi không thể làm cho nó hoạt động. Tôi đã đọc một số bài đăng liên quan đến vấn đề này và một số người trong số họ nói rằng bộ chọn cần được đặt ở bộ điều khiển chế độ xem chính, nhưng đối với tôi, nó vẫn không hoạt động ... Tôi có thể làm gì sai?

Cảm ơn


nó có đủ tốt để đặt mã bạn cần trong viewWillDisappear không?
DogCoffee

1
Sử dụng các phương pháp trên UINavigationControllerDelegate.
Mike Weller

@Smick Không, không may rằng sẽ không đủ trong kịch bản của tôi ...
AppsDev

@MikeWeller tôi đã cố gắng nhưng tôi không thể làm cho nó hoạt
AppsDev

Kiểm tra câu trả lời trong câu hỏi này. Giải pháp tốt nhất tôi đã tìm thấy. stackoverflow.com/questions/1214965/…
James Parker

Câu trả lời:


132

Hãy thử mã này bằng cách sử dụng VIewWillDisappearphương pháp để phát hiện việc nhấn nút quay lại của NavigationItem:

-(void) viewWillDisappear:(BOOL)animated
{
    if ([self.navigationController.viewControllers indexOfObject:self]==NSNotFound) 
    {
        // Navigation button was pressed. Do some stuff 
        [self.navigationController popViewControllerAnimated:NO];
    }
    [super viewWillDisappear:animated];
}

HOẶC Có một cách khác để có được Hành động của nút Điều hướng BAck.

Tạo nút Tùy chỉnh cho UINavigationItem của nút quay lại.

Đối với ví dụ:

Trong ViewDidLoad:

- (void)viewDidLoad 
{
    [super viewDidLoad];
    UIBarButtonItem *newBackButton = [[UIBarButtonItem alloc] initWithTitle:@"Home" style:UIBarButtonItemStyleBordered target:self action:@selector(home:)];
    self.navigationItem.leftBarButtonItem=newBackButton;
}

-(void)home:(UIBarButtonItem *)sender 
{
    [self.navigationController popToRootViewControllerAnimated:YES];
}

Swift:

override func willMoveToParentViewController(parent: UIViewController?) 
{
    if parent == nil 
    {
        // Back btn Event handler
    }
}

3
Tôi không nghĩ bạn cần [self.navigationController popViewControllerAnimated: NO] trong viewWillDisappear.
Robert Kang

2
Tôi nghĩ rằng phương pháp này không có tác dụng nữa với iOS 8 như self.navigationController.viewControllers chỉ chứa một phần tử, == tự
Sébastien Stormacq

1
@ Sébastien Stormacq Tại sao bạn lại nói như vậy? Nó hoạt động trong iOS 8.
蘇健豪

1
Nếu bạn gọi nó trong ViewWillDisappear, nó sẽ không được gọi chỉ khi nhấn vào nút quay lại. Nó sẽ được gọi bất cứ khi nào VC đang được popped hoặc bất cứ khi nào một VC mới đang được đẩy
NSNoob

41

Nhanh

override func didMoveToParentViewController(parent: UIViewController?) {
    if parent == nil {
        //"Back pressed"
    }
}

5
Vấn đề duy nhất với giải pháp này là nếu bạn vuốt để quay lại và thay đổi ý định, điều này sẽ được kích hoạt.
chris P

18

Có lẽ câu trả lời này không phù hợp với lời giải thích của bạn ngoài tiêu đề câu hỏi. Nó hữu ích khi bạn đang cố gắng biết khi nào bạn nhấn vào nút quay lại trên UINavigationBar.

Trong trường hợp này, bạn có thể sử dụng UINavigationBarDelegategiao thức và triển khai một trong các phương pháp sau:

- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item;
- (void)navigationBar:(UINavigationBar *)navigationBar didPopItem:(UINavigationItem *)item;

Khi didPopItemphương thức được gọi, đó là do bạn đã nhấn vào nút quay lại hoặc bạn đã sử dụng [UINavigationBar popNavigationItemAnimated:]phương thức và thanh điều hướng đã bật mục.

Bây giờ, nếu bạn muốn biết hành động nào đã kích hoạt didPopItemphương pháp, bạn có thể sử dụng một cờ.

Với cách tiếp cận này, tôi không cần phải thêm mục nút thanh bên trái có hình mũi tên theo cách thủ công để làm cho nó tương tự như nút quay lại iOS và có thể đặt mục tiêu / hành động tùy chỉnh của mình.


Hãy xem một ví dụ:

Tôi có bộ điều khiển chế độ xem có bộ điều khiển chế độ xem trang và chế độ xem chỉ báo trang tùy chỉnh. Tôi cũng đang sử dụng UINavigationBar tùy chỉnh để hiển thị tiêu đề để biết tôi đang ở trang nào và nút quay lại để quay lại trang trước. Và tôi cũng có thể vuốt sang trang trước / sau trên bộ điều khiển trang.

#pragma mark - UIPageViewController Delegate Methods
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed {

    if( completed ) {

        //...

        if( currentIndex > lastIndex ) {

            UINavigationItem *navigationItem = [[UINavigationItem alloc] initWithTitle:@"Some page title"];

            [[_someViewController navigationBar] pushNavigationItem:navigationItem animated:YES];
            [[_someViewController pageControl] setCurrentPage:currentIndex];
        } else {
            _autoPop = YES; //We pop the item automatically from code.
            [[_someViewController navigationBar] popNavigationItemAnimated:YES];
            [[_someViewController pageControl] setCurrentPage:currentIndex];
        }
    }

}

Vì vậy, sau đó tôi triển khai các phương thức ủy quyền UINavigationBar:

#pragma mark - UINavigationBar Delegate Methods
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item {
    if( !_autoPop ) {
        //Pop by back button tap
    } else {
        //Pop from code
    }

    _autoPop = NO;

    return YES;
}

Trong trường hợp này, tôi đã sử dụng shouldPopItemvì cửa sổ bật lên hoạt hình và tôi muốn xử lý nút quay lại ngay lập tức và không đợi cho đến khi quá trình chuyển đổi hoàn tất.


12

Vấn đề với didMoveToParentViewControllernó là nó được gọi khi chế độ xem gốc hiển thị lại hoàn toàn, vì vậy nếu bạn cần thực hiện một số tác vụ trước đó, nó sẽ không hoạt động.

Và nó không hoạt động với cử chỉ hoạt hình được điều khiển. Sử dụng willMoveToParentViewControllerhoạt động tốt hơn.

Objective-c

- (void)willMoveToParentViewController:(UIViewController *)parent{
    if (parent == NULL) {
        // ...
    }
}

Nhanh

override func willMoveToParentViewController(parent: UIViewController?) {
    if parent == nil {
        // ...  
    }
}

6

Đây là phiên bản Objective-C của Câu trả lời của dadachi :

Objective-C

- (void)didMoveToParentViewController:(UIViewController *)parent{
    if (parent == NULL) {
        NSLog(@"Back Pressed");
    }
}

3

Đặt đại biểu của UINavigationBar, rồi sử dụng:

- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item {
    //handle the action here
}

2
Nếu bạn đang sử dụng một UINavigationControllerđể quản lý thanh điều hướng thì việc cố gắng đặt người ủy quyền sẽ gây ra ngoại lệ: "*** Việc chấm dứt ứng dụng do không có ngoại lệ 'NSInternalInconsistencyException', lý do: 'Không thể đặt người ủy quyền theo cách thủ công trên Thanh UINavigationBar do bộ điều khiển quản lý. ''. Là UINavigationControllerđại biểu. Có nghĩa là bạn có thể phân lớp bộ điều khiển và ghi đè các UINavigationBarDelegatephương thức (có thể gọi là super).
Geoff Hackworth

Nhưng bạn không thể gọi trực tiếp super vì UINavigationControllerkhông tuân theo công khai UINavigationBarDelegate, dẫn đến lỗi trình biên dịch! Có thể có một giải pháp sử dụng UINavigationControllerDelegate.
Geoff Hackworth

3

Không có giải pháp nào khác phù hợp với tôi, nhưng điều này làm được:

Tạo lớp con UINavigationController của riêng bạn, làm cho nó triển khai UINavigationBarDelegate (không cần đặt đại biểu của thanh điều hướng theo cách thủ công), thêm phần mở rộng UIViewController xác định một phương thức được gọi khi nhấn nút quay lại, rồi triển khai phương thức này trong lớp con UINavigationController của bạn :

func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
    self.topViewController?.methodToBeCalledOnBackButtonPress()
    self.popViewController(animated: true)
    return true
}

Bạn có thể mở rộng câu trả lời của mình và chỉ ra cách chính xác để sử dụng nó trong bộ điều khiển chế độ xem.
zeeshan

3

Trong Swift 4 trở lên:

override func didMove(toParent parent: UIViewController?) {
    if parent == nil {
        //"Back pressed"
    }
}

2

Đặt UINavigationControllerDelegate và triển khai hàm đại biểu này (Swift):

func navigationController(navigationController: UINavigationController, willShowViewController viewController: UIViewController, animated: Bool) {
    if viewController is <target class> {
        //if the only way to get back - back button was pressed
    }
}

1

Sử dụng một UINavigationControllerlớp con tùy chỉnh , lớp này triển khaishouldPop phương thức.

Trong Swift:

class NavigationController: UINavigationController, UINavigationBarDelegate
{
    var shouldPopHandler: (() -> Bool)?

    func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool
    {
        if let shouldPopHandler = self.shouldPopHandler, !shouldPopHandler()
        {
            return false
        }
        self.popViewController(animated: true) // Needed!
        return true
    }
}

Khi thiết lập, shouldPopHandler() sẽ được gọi để quyết định xem bộ điều khiển có bật lên hay không. Khi không được thiết lập, nó sẽ xuất hiện như bình thường.

Bạn nên tắt UINavigationControllers interactivePopGestureRecognizervì cử chỉ sẽ không gọi trình xử lý của bạn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.