Thay đổi nút quay lại trong iOS 7 vô hiệu hóa thao tác vuốt để điều hướng quay lại


80

Tôi có một ứng dụng iOS 7 nơi tôi đang đặt nút quay lại tùy chỉnh như sau:

    UIImage *backButtonImage = [UIImage imageNamed:@"back-button"];
    UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];

    [backButton setImage:backButtonImage forState:UIControlStateNormal];
    backButton.frame = CGRectMake(0, 0, 20, 20);

    [backButton addTarget:self
                   action:@selector(popViewController)
         forControlEvents:UIControlEventTouchUpInside];

    UIBarButtonItem *backBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
    viewController.navigationItem.leftBarButtonItem = backBarButtonItem;

Nhưng điều này vô hiệu hóa cử chỉ "vuốt từ trái sang phải" của iOS 7 để điều hướng đến bộ điều khiển trước đó. Có ai biết cách tôi có thể đặt nút tùy chỉnh và vẫn bật cử chỉ này không?

CHỈNH SỬA: Tôi đã cố gắng đặt viewController.navigationItem.backBarButtonItem thay thế, nhưng điều này dường như không hiển thị hình ảnh tùy chỉnh của tôi.


Tôi vẫn chưa tìm thấy một giải pháp thích hợp cho điều này ?? Có ai đã tìm thấy một giải pháp tốt và giải thích tại sao điều đó đang hoạt động ??.
user1010819

Làm thế nào về việc sử dụng thư viện của bên thứ ba được tạo tốt: SwipeBack ?
devxoul

Câu trả lời:


82

QUAN TRỌNG: Đây là một vụ hack. Tôi khuyên bạn nên xem câu trả lời này .

Gọi dòng sau sau khi giao công leftBarButtonItemviệc cho tôi:

self.navigationController.interactivePopGestureRecognizer.delegate = self;

Chỉnh sửa: Điều này không hoạt động nếu được gọi trong initcác phương thức. Nó phải được gọi trong viewDidLoadhoặc các phương thức tương tự.


Bạn có đặt điều này trên bộ điều khiển chế độ xem không? hoặc bộ điều khiển điều hướng? Tôi có cùng một vấn đề nhưng điều này dường như không hoạt động?
Kevin Renskers,

1
@mixedCase Sau khi tải xuống dự án mẫu của bạn, giờ tôi đã hiểu vấn đề của bạn. Mã hoạt động miễn là nội dung của chế độ xem bộ sưu tập không vượt quá chiều ngang của bộ điều khiển chế độ xem. Tuy nhiên, ngay khi chế độ xem bộ sưu tập có thể cuộn theo chiều ngang, nó sẽ ghi đè lên tương tácPopGestureRecognizer. Tôi sẽ xem nếu tôi có thể tìm thấy một công việc xung quanh.
Paul Hunter

8
Tôi có vấn đề với mã này. Nó hoạt động không lâu, sau 5-10 lần vuốt ngược VC bị đóng băng. Bất kì giải pháp nào?
Timur Bernikovich

1
Timur Bernikowich Tôi cũng gặp trường hợp tương tự. Bạn có tìm thấy lý do nào cho điều này không?
user1010819

4
Đặt ủy quyền để selfhủy đặt nó khỏi một đối tượng của lớp _UINavigationInteractiveTransition. Đối tượng đó có trách nhiệm đảm bảo rằng bộ điều khiển điều hướng không được yêu cầu bật lên khi nó đang trong quá trình chuyển đổi. Vẫn đang điều tra xem liệu có thể bật cử chỉ này hay không khi nút quay lại được tùy chỉnh.
Saltymule

56

Sử dụng các thuộc tính backIndicatorImage và backIndicatorTransitionMaskImage của UINavigationBar nếu có thể. Đặt chúng trên một UIAppearanceProxy có thể dễ dàng sửa đổi hành vi trên ứng dụng của bạn. Điểm khó khăn là bạn chỉ có thể thiết lập những điều đó trên ios 7, nhưng điều đó hiệu quả vì bạn chỉ có thể sử dụng cử chỉ bật trên iOS 7. Kiểu dáng ios 6 bình thường của bạn có thể vẫn còn nguyên vẹn.

UINavigationBar* appearanceNavigationBar = [UINavigationBar appearance];
//the appearanceProxy returns NO, so ask the class directly
if ([[UINavigationBar class] instancesRespondToSelector:@selector(setBackIndicatorImage:)])
{
    appearanceNavigationBar.backIndicatorImage = [UIImage imageNamed:@"back"];
    appearanceNavigationBar.backIndicatorTransitionMaskImage = [UIImage imageNamed:@"back"];
    //sets back button color
    appearanceNavigationBar.tintColor = [UIColor whiteColor];
}else{
    //do ios 6 customization
}

Cố gắng thao túng đại biểu của tương tác PopGestureRecognizer sẽ dẫn đến rất nhiều vấn đề.


4
Cảm ơn Dan, điều này thực sự quan trọng và phải là câu trả lời được chấp nhận. Chỉ cần chỉ định lại người được ủy quyền, bạn có thể thấy RẤT NHIỀU hành vi kỳ lạ. Đặc biệt khi người dùng cố gắng vuốt lại trên topViewController.
Chris Wagner

1
Đây là một giải pháp giả định rằng tất cả những gì bạn muốn làm là thay đổi hình ảnh nút quay lại. Nhưng nếu bạn muốn thay đổi văn bản của nút quay lại và / hoặc hành động được thực hiện khi nhấp vào nút quay lại thì sao?
user102008 Ngày

Tại thời điểm đó, bạn sẽ được phục vụ tốt hơn bằng cách đặt UINavigationItem.leftBarButtonItem. Có nhiều câu trả lời có thể được tìm thấy bằng cách tìm kiếm leftBarButtonItem trên google hoặc stackoverflow.
Saltymule

Nếu bạn muốn giới hạn sự thay đổi diện mạo để UI của riêng bạn và không ảnh hưởng đến thanh điều hướng là ví dụ tạo ra bởi ABPeoplePickerNavigationControllerbạn có thể sử dụng một tùy chỉnh UINavigationControllerlớp con:[[UINavigationBar appearanceWhenContainedIn:[THNavigationController class], nil] setBackIndicatorImage:[UIImage imageNamed:@"btn_back_arrow"]]; [[UINavigationBar appearanceWhenContainedIn:[THNavigationController class], nil] setBackIndicatorTransitionMaskImage:[UIImage imageNamed:@"btn_back_arrow_highlighted"]];
tom

2
Thật không may, điều này không hoạt động trên iOS8. Nút quay lại không thay đổi hình thức của nó thành hình ảnh tùy chỉnh của tôi.
Lensflare

29

Tôi đã thấy giải pháp này http://keighl.com/post/ios7-interactive-pop-gesture-custom-back-button/ phân lớp UINavigationController. Đó là một giải pháp tốt hơn vì nó xử lý trường hợp bạn vuốt trước khi bộ điều khiển ở đúng vị trí - điều này gây ra sự cố.

Ngoài ra, tôi nhận thấy nếu bạn thực hiện thao tác vuốt trên bộ điều khiển chế độ xem gốc (sau khi nhấn vào một và quay lại), giao diện người dùng sẽ không phản hồi (cũng có vấn đề tương tự trong câu trả lời ở trên).

Vì vậy, mã trong UINavigationController lớp con sẽ trông giống như sau:

@implementation NavigationController

- (void)viewDidLoad {
    [super viewDidLoad];
    __weak NavigationController *weakSelf = self;

    if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
        self.interactivePopGestureRecognizer.delegate = weakSelf;
        self.delegate = weakSelf;
    }
}

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
    // Hijack the push method to disable the gesture
    if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
        self.interactivePopGestureRecognizer.enabled = NO;
    }
    [super pushViewController:viewController animated:animated];
}

#pragma mark - UINavigationControllerDelegate

- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animate {
    // Enable the gesture again once the new controller is shown
    self.interactivePopGestureRecognizer.enabled = ([self respondsToSelector:@selector(interactivePopGestureRecognizer)] && [self.viewControllers count] > 1);
}

@end

1
đã gặp vấn đề tương tự với thao tác vuốt trên bộ điều khiển chế độ xem gốc, cảm ơn!
Michael Rose

GIẢI PHÁP TỐT NHẤT TRONG TOÀN TRANG. CÔNG VIỆC HOÀN HẢO. THANKS
Shahid Iqbal

19

tôi sử dụng

[[UINavigationBar appearance] setBackIndicatorImage:[UIImage imageNamed:@"nav_back.png"]];
[[UINavigationBar appearance] setBackIndicatorTransitionMaskImage:[UIImage imageNamed:@"nav_back.png"]];

[UIBarButtonItem.appearance setBackButtonTitlePositionAdjustment:UIOffsetMake(0, -64) forBarMetrics:UIBarMetricsDefault];

2
Tôi đã thấy ý kiến ở những nơi khác mà điều này gây ra vấn đề trong chế độ 64 bit do một lỗi trong mã của Apple tại present- chỉ nghĩ rằng tôi muốn gửi một lời cảnh báo
Peter Johnson

@PeterJohnson bị lỗi gì vậy?
art-divin

Đơn giản là giải pháp tốt nhất!
Igotit

Giải pháp đơn giản nhất từ ​​trước đến nay.
tounaobun

6

Tôi cũng ẩn nút quay lại, thay thế nó bằng một leftBarItem tùy chỉnh.
Xóa đại biểu tương tácPopGestureRecognizer sau khi hành động đẩy hoạt động với tôi:

[self.navigationController pushViewController:vcToPush animated:YES];

// Enabling iOS 7 screen-edge-pan-gesture for pop action
if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
    self.navigationController.interactivePopGestureRecognizer.delegate = nil;
}

4
Một vấn đề với phương pháp này là nếu bạn thực hiện xoay cạnh trên chế độ xem gốc, nó sẽ khóa giao diện người dùng. Tôi gặp phải điều này vì tôi đang đẩy nhiều bản sao của cùng một chế độ xem vào ngăn xếp điều hướng.
Nikola Lajic,

@MrNickBarker Cảm ơn bạn đã cho tôi biết! Bạn có thể vui lòng mô tả kịch bản chính xác? Tôi không thể tái tạo nó khi trượt điều khiển xem gốc
avishic

4
Tôi đã đặt nó trong phương thức viewDidLoad. Giải pháp cuối cùng của tôi là đặt một lớp khác làm đại biểu mà chỉ trả về true cho 'scrollRecognizerShouldBegin' nếu có nhiều hơn một bộ điều khiển chế độ xem trong ngăn xếp điều hướng.
Nikola Lajic,

MrNickBarker bất kỳ lý do tại sao nó bị đóng băng Tôi đang trải qua cùng
user1010819

6
navigationController.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)self;

Đây là từ http://stuartkhall.com/posts/ios-7-development-tips-tricks-hacks , nhưng nó gây ra một số lỗi:

  1. Đẩy một viewController khác vào navigationController khi vuốt vào từ cạnh trái của màn hình;
  2. Hoặc, vuốt vào từ cạnh trái của màn hình khi topViewController bật lên từ navigationController;

ví dụ: Khi rootViewController của navigationController đang hiển thị, hãy vuốt vào từ cạnh trái của màn hình và chạm vào một cái gì đó (NHANH CHÓNG) để đẩy mộtViewController khác vào navigationController, sau đó

  • RootViewController không phản hồi bất kỳ sự kiện chạm nào;
  • OtherViewController sẽ không được hiển thị;
  • Vuốt lại từ cạnh của màn hình, mộtViewController khác sẽ được hiển thị;
  • Nhấn vào nút quay lại tùy chỉnh để bậtViewController khác, sự cố!

Vì vậy, bạn phải triển khai UIGestureRecognizerDelegatephương thức self.navigationController.interactivePopGestureRecognizer.delegatenhư sau:

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
    if (gestureRecognizer == navigationController.interactivePopGestureRecognizer) {
        return !navigationController.<#TODO: isPushAnimating#> && [navigationController.viewControllers count] > 1;
    }
    return YES;
}

1
SwipeBack là một giải pháp cho những vấn đề này.
devxoul

6

Đây là câu trả lời của Nick H247 phiên bản swift3

class NavigationController: UINavigationController {
  override func viewDidLoad() {
    super.viewDidLoad()
    if responds(to: #selector(getter: interactivePopGestureRecognizer)) {
      interactivePopGestureRecognizer?.delegate = self
      delegate = self
    }
  }

  override func pushViewController(_ viewController: UIViewController, animated: Bool) {
    if responds(to: #selector(getter: interactivePopGestureRecognizer)) {
      interactivePopGestureRecognizer?.isEnabled = false
    }
    super.pushViewController(viewController, animated: animated)
  }
}

extension NavigationController: UINavigationControllerDelegate {
  func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
    interactivePopGestureRecognizer?.isEnabled = (responds(to: #selector(getter: interactivePopGestureRecognizer)) && viewControllers.count > 1)
  }
}

extension NavigationController: UIGestureRecognizerDelegate {}

3

Thử self.navigationController.interactivePopGestureRecognizer.enabled = YES;


Không, nó đã được bật .. vấn đề có vẻ là người đại diện của nó không cho phép trình nhận dạng cử chỉ khởi động, tôi đã thêm giải pháp của mình làm câu trả lời khác ở đây.
avishic

avishic bạn có thể vui lòng giải thích lý do tại sao thiết lập ủy quyền sẽ hoạt động? .. Tôi đang mắc kẹt về cùng một vấn đề.
user1010819

1

Tôi đã không viết điều này, nhưng blog sau đây đã giúp rất nhiều và giải quyết vấn đề của tôi với nút điều hướng tùy chỉnh:

http://keighl.com/post/ios7-interactive-pop-gesture-custom-back-button/

Tóm lại, anh ta triển khai một UINavigationController tùy chỉnh sử dụng đại biểu cử chỉ pop. Rất sạch sẽ và di động!

Mã:

@interface CBNavigationController : UINavigationController <UINavigationControllerDelegate, UIGestureRecognizerDelegate>
@end

@implementation CBNavigationController

- (void)viewDidLoad
{
  __weak CBNavigationController *weakSelf = self;

  if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)])
  {
    self.interactivePopGestureRecognizer.delegate = weakSelf;
    self.delegate = weakSelf;
  }
}

// Hijack the push method to disable the gesture

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
  if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)])
    self.interactivePopGestureRecognizer.enabled = NO;

  [super pushViewController:viewController animated:animated];
}

#pragma mark UINavigationControllerDelegate

- (void)navigationController:(UINavigationController *)navigationController
       didShowViewController:(UIViewController *)viewController
                    animated:(BOOL)animate
{
  // Enable the gesture again once the new controller is shown

  if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)])
    self.interactivePopGestureRecognizer.enabled = YES;
}

Biên tập. Đã thêm bản sửa lỗi cho các sự cố khi người dùng cố vuốt sang trái trên bộ điều khiển chế độ xem gốc:

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {

    if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)] &&
        self.topViewController == [self.viewControllers firstObject] &&
        gestureRecognizer == self.interactivePopGestureRecognizer) {

        return NO;
    }

    return YES;
}

Giao diện người dùng bị đóng băng nếu một người vuốt sang trái trên bộ điều khiển chế độ xem gốc của bộ điều khiển Điều hướng.
JohnVanDijk,

@JohnVanDijk Tôi đã chỉnh sửa câu trả lời với một bản sửa lỗi mà tôi nghĩ rằng tôi đã triển khai để giải quyết vấn đề đó. Đã được một thời gian, nhưng nó có ý nghĩa. Về cơ bản, nếu bộ điều khiển nhìn từ trên xuống là bộ điều khiển xem gốc, sau đó chúng tôi sẽ không đáp ứng với các 'interactivePopGestureRecognizer'
kgaidis

Nó đang ẩn nút quay lại của tôi
Mohammed Hussain

1

RootView

override func viewDidAppear(_ animated: Bool) {
    self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false
}

ChildView

override func viewDidLoad() {
    self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true
    self.navigationController?.interactivePopGestureRecognizer?.delegate = self
} 

extension ChildViewController: UIGestureRecognizerDelegate {}

1

Sử dụng logic này để tiếp tục bật hoặc tắt cử chỉ vuốt ..

- (void)navigationController:(UINavigationController *)navigationController
       didShowViewController:(UIViewController *)viewController
                    animated:(BOOL)animate
{
    if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)])
    {
        if (self.navigationController.viewControllers.count > 1)
        {
            self.navigationController.interactivePopGestureRecognizer.enabled = YES;
        }
        else
        {
            self.navigationController.interactivePopGestureRecognizer.enabled = NO;
        }
    }
}

0

Tôi đã gặp sự cố tương tự khi tôi chỉ định bộ điều khiển chế độ xem hiện tại làm đại biểu cho cử chỉ pop tương tác, nhưng sẽ phá vỡ cử chỉ trên bất kỳ chế độ xem nào được đẩy hoặc các chế độ xem bên dưới chế độ xem trong ngăn xếp điều hướng. Cách tôi giải quyết vấn đề này là đặt ủy quyền vào -viewDidAppear, sau đó đặt nó thành nil -viewWillDisappear. Điều đó cho phép các quan điểm khác của tôi hoạt động chính xác.


0

Hãy tưởng tượng chúng ta đang sử dụng mẫu dự án chính / chi tiết mặc định của Apple, trong đó cái chính là bộ điều khiển chế độ xem bảng và chạm vào nó sẽ hiển thị bộ điều khiển chế độ xem chi tiết.

Chúng tôi muốn tùy chỉnh nút quay lại xuất hiện trong bộ điều khiển chế độ xem chi tiết. Đây là cách tùy chỉnh hình ảnh , màu ảnh , chữ , màu chữ, phông chữ của nút quay lại.


Để thay đổi hình ảnh, màu hình ảnh, màu văn bản hoặc phông chữ trên toàn cầu, hãy đặt phần sau vào vị trí được gọi trước khi bất kỳ bộ điều khiển chế độ xem nào của bạn được tạo (ví dụ: application:didFinishLaunchingWithOptions:là một nơi tốt).

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    UINavigationBar* navigationBarAppearance = [UINavigationBar appearance];

    // change the back button, using default tint color
    navigationBarAppearance.backIndicatorImage = [UIImage imageNamed:@"back"];
    navigationBarAppearance.backIndicatorTransitionMaskImage = [UIImage imageNamed:@"back"];

    // change the back button, using the color inside the original image
    navigationBarAppearance.backIndicatorImage = [[UIImage imageNamed:@"back"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
    navigationBarAppearance.backIndicatorTransitionMaskImage = [UIImage imageNamed:@"back"];

    // change the tint color of everything in a navigation bar
    navigationBarAppearance.tintColor = [UIColor greenColor];

    // change the font in all toolbar buttons
    NSDictionary *barButtonTitleTextAttributes =
    @{
      NSFontAttributeName: [UIFont fontWithName:@"HelveticaNeue-Light" size:12.0],
      NSForegroundColorAttributeName: [UIColor purpleColor]
      };

    [[UIBarButtonItem appearance] setTitleTextAttributes:barButtonTitleTextAttributes forState:UIControlStateNormal];

    return YES;
}

Lưu ý, bạn có thể sử dụng appearanceWhenContainedIn:để có nhiều quyền kiểm soát hơn đối với bộ điều khiển chế độ xem nào bị ảnh hưởng bởi những thay đổi này, nhưng hãy nhớ rằng bạn không thể vượt qua [DetailViewController class], vì nó được chứa bên trong UINavigationController, không phải DetailViewController của bạn. Điều này có nghĩa là bạn sẽ cần phân lớp UINavigationController nếu bạn muốn kiểm soát nhiều hơn những gì bị ảnh hưởng.

Để tùy chỉnh văn bản hoặc phông chữ / màu sắc của một mục nút quay lại cụ thể, bạn phải làm như vậy trong MasterViewController (không phải DetailViewController!). Điều này có vẻ không trực quan vì nút xuất hiện trên DetailViewController. Tuy nhiên, một khi bạn hiểu rằng cách tùy chỉnh nó là bằng cách đặt một thuộc tính trên một navigationItem, thì nó bắt đầu có ý nghĩa hơn.

- (void)viewDidLoad { // MASTER view controller
    [super viewDidLoad];

    UIBarButtonItem *buttonItem = [[UIBarButtonItem alloc] initWithTitle:@"Testing"
                                                                   style:UIBarButtonItemStylePlain
                                                                  target:nil
                                                                  action:nil];
    NSDictionary *barButtonTitleTextAttributes =
    @{
      NSFontAttributeName: [UIFont fontWithName:@"HelveticaNeue-Light" size:12.0],
      NSForegroundColorAttributeName: [UIColor purpleColor]
      };
    [buttonItem setTitleTextAttributes:barButtonTitleTextAttributes forState:UIControlStateNormal];
    self.navigationItem.backBarButtonItem = buttonItem;
}

Lưu ý: cố gắng đặt titleTextAttributes sau khi đặt self.navigationItem.backBarButtonItem dường như không hoạt động, vì vậy chúng phải được đặt trước khi bạn gán giá trị cho thuộc tính này.


0

Tạo một lớp 'TTNavigationViewController' là lớp con của 'UINavigationController' và đặt bộ điều khiển điều hướng hiện có của bạn thuộc lớp này trong bảng phân cảnh / lớp, Mã ví dụ trong lớp -

    class TTNavigationViewController: UINavigationController, UIGestureRecognizerDelegate {

override func viewDidLoad() {
    super.viewDidLoad()
    self.setNavigationBarHidden(true, animated: false)

    // enable slide-back
    if self.responds(to: #selector(getter: UINavigationController.interactivePopGestureRecognizer)) {
        self.interactivePopGestureRecognizer?.isEnabled = true
        self.interactivePopGestureRecognizer?.delegate  = self
    }
}

func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
    return true
}}
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.