Zmiana przycisku Wstecz w systemie iOS 7 wyłącza przesunięcie, aby nawigować wstecz

Mam aplikację na iOS 7, w której ustawiam niestandardowy przycisk Wstecz w ten sposób:

    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;

Ale to wyłącza gest "Przesuń palcem od lewej do prawej" systemu iOS 7, aby przejść do poprzedniego kontrolera. Czy ktoś wie, jak Mogę ustawić niestandardowy przycisk i nadal utrzymać ten gest włączony?

Edytuj: Próbowałem ustawić viewController.navigationItem.backBarButtonItem zamiast, ale to nie wydaje się pokazać mój niestandardowy obraz.

Author: RyanR, 2013-09-27

13 answers

Ważne: To jest włamanie. Polecam spojrzeć na tę odpowiedź .

Wywołanie następującej linii po przypisaniu leftBarButtonItem zadziałało dla mnie:

self.navigationController.interactivePopGestureRecognizer.delegate = self;

Edit: Nie działa to w przypadku wywołania metod init. Powinien być wywoływany w viewDidLoad lub podobnych metodach.

 76
Author: Paul Hunter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:46

Użyj właściwości backIndicatorImage i backIndicatorTransitionMaskImage paska UINavigationBar, jeśli to możliwe. Ustawienie ich na interfejsie a UIAppearanceProxy może łatwo modyfikować zachowanie w całej aplikacji. Zmarszczka polega na tym, że możesz ustawić tylko te na ios 7, ale to działa, ponieważ możesz używać tylko gestu pop na ios 7 i tak. Twoja normalna stylizacja ios 6 może pozostać nienaruszona.

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
}

Próba manipulowania delegatem interactivePopGestureRecognizer doprowadzi do wielu problemów.

 56
Author: Saltymule,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-02 14:35:17

Widziałem to rozwiązanie http://keighl.com/post/ios7-interactive-pop-gesture-custom-back-button / który podklasa UINavigationController. Jest to lepsze rozwiązanie, ponieważ obsługuje sprawę, w której przesuwasz palcem, zanim kontroler jest na miejscu - co powoduje awarię.

Poza tym zauważyłem, że jeśli wykonasz machnięcie na kontrolerze widoku głównego (po naciśnięciu na jeden i z powrotem) interfejs przestanie odpowiadać (również ten sam problem w odpowiedzi powyżej).

Więc kod w podklasowany UINavigationController powinien wyglądać tak:

@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
 28
Author: Nick H247,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-27 13:20:19

Używam

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

[UIBarButtonItem.appearance setBackButtonTitlePositionAdjustment:UIOffsetMake(0, -64) forBarMetrics:UIBarMetricsDefault];
 19
Author: Albert Chu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-05 09:37:27

Ukrywam również przycisk back, zastępując go niestandardowym leftBarItem.
Usunięcie delegata interactivePopGestureRecognizer po akcji push działało dla mnie:

[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;
}
 6
Author: avishic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-02 09:18:47
navigationController.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)self;

To jest z http://stuartkhall.com/posts/ios-7-development-tips-tricks-hacks , ale powoduje kilka błędów:

  1. wciśnij inny kontroler viewController do navigationController, przesuwając od lewej krawędzi ekranu;
  2. W przypadku, gdy z poziomu navigationController pojawia się topViewController, przesuń palcem od lewej krawędzi ekranu.]}

Np. gdy wyświetla się rootViewController navigationController, przesuń palcem od lewej w tym celu należy kliknąć na ikonę, a następnie kliknąć na ikonę, aby wcisnąć inny kontroler ViewController do kontrolera navigationController, a następnie

    RootViewController nie odpowiada na żadne zdarzenie dotykowe;
  • kontroler anotherViewController nie zostanie pokazany;
  • przesuń palcem od krawędzi ekranu ponownie, wyświetlony zostanie kontroler anotherViewController;
  • naciśnij niestandardowy przycisk Wstecz, aby pop anotherViewController, crash!

Więc musisz zaimplementować UIGestureRecognizerDelegate metodę w self.navigationController.interactivePopGestureRecognizer.delegate Jak to:

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
    if (gestureRecognizer == navigationController.interactivePopGestureRecognizer) {
        return !navigationController.<#TODO: isPushAnimating#> && [navigationController.viewControllers count] > 1;
    }
    return YES;
}
 6
Author: iwill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-12 03:22:40

Oto wersja swift3 Nick h247 ' s answer

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 {}
 6
Author: duan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:55:01

Spróbuj self.navigationController.interactivePopGestureRecognizer.enabled = YES;

 3
Author: ilya n.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-27 16:42:50

Nie pisałem tego, ale poniższy blog bardzo pomógł i rozwiązał moje problemy z niestandardowym przyciskiem nawigacyjnym:

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

Podsumowując, implementuje Niestandardowy Kontroler UINavigationController, który używa delegata gestów pop. Bardzo czysty i przenośny!

Kod:

@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;
}

Edit. Dodano poprawkę dla problemów, gdy użytkownik próbuje przesunąć palcem w lewo na kontrolerze widoku głównego:

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

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

        return NO;
    }

    return YES;
}
 1
Author: kgaidis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-29 14:22:02

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
Author: PW486,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-08 04:50:52

Miałem podobny problem, gdy przypisywałem bieżący kontroler widoku jako delegata do interaktywnego gestu pop, ale łamałem gest na dowolnych widokach pchanych lub widokach pod widokiem w stosie nav. Sposób, w jaki to rozwiązałem, polegał na ustawieniu delegata w -viewDidAppear, a następnie ustawieniu go na nil w -viewWillDisappear. To pozwoliło na poprawne działanie moich innych poglądów.

 0
Author: Bill Burgess,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-06 22:33:12

Wyobraź sobie, że używamy domyślnego szablonu projektu master/detail firmy Apple, gdzie master jest kontrolerem widoku tabeli, a dotknięcie go pokaże kontroler widoku detail.

Chcemy dostosować przycisk Wstecz, który pojawi się w kontrolerze widoku szczegółów. Oto jak dostosować obraz , kolor obrazu, tekst , kolor tekstu i czcionka przycisku Wstecz.


Aby zmienić obraz, kolor obrazu, kolor tekstu lub czcionkę globalnie, umieść następnie w miejscu, które jest wywołane przed utworzeniem dowolnego kontrolera widoku (np. application:didFinishLaunchingWithOptions: jest dobrym miejscem).

- (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;
}

Uwaga, możesz użyć appearanceWhenContainedIn:, aby mieć większą kontrolę nad tym, które Kontrolery widoku są dotknięte tymi zmianami, ale pamiętaj, że nie możesz przekazać [DetailViewController class], ponieważ jest on zawarty wewnątrz kontrolera Uinavigationcontroller, a nie DetailViewController. Oznacza to, że będziesz musiał podklasować UINavigationController, jeśli chcesz mieć większą kontrolę nad tym, co jest dotknięte.

Aby dostosować tekst lub czcionkę/kolor określonego elementu przycisku Wstecz, musisz to zrobić w MasterViewController (Nie w DetailViewController!). Wydaje się to nieintuicyjne, ponieważ przycisk pojawia się w DetailViewController. Jednak gdy zrozumiesz, że sposobem dostosowania go jest ustawienie właściwości na navigationItem, zaczyna to mieć większy sens.

- (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;
}

Uwaga: próba ustawienia titleTextAttributes po ustawieniu siebie.navigationItem.backBarButtonItem nie działa, więc muszą być ustawione przed przypisaniem wartości do tej właściwości.

 0
Author: Senseful,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:46

Utwórz klasę 'TTNavigationViewController', która jest podklasą 'UINavigationController' i stwórz istniejący Kontroler nawigacyjny tej klasy albo w storyboard/class, przykładowy kod w class -

    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
}}
 0
Author: Prakash Raj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-19 10:30:20