Durch Ändern der Zurück-Schaltfläche in iOS 7 wird das Wischen deaktiviert, um zurück zu navigieren


80

Ich habe eine iOS 7-App, in der ich eine benutzerdefinierte Zurück-Schaltfläche wie folgt einstelle:

    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;

Dadurch wird jedoch die iOS 7-Geste "Von links nach rechts wischen" deaktiviert, um zum vorherigen Controller zu navigieren. Weiß jemand, wie ich eine benutzerdefinierte Schaltfläche festlegen und diese Geste trotzdem aktivieren kann?

BEARBEITEN: Ich habe versucht, stattdessen viewController.navigationItem.backBarButtonItem festzulegen, aber dies scheint mein benutzerdefiniertes Bild nicht anzuzeigen.


Ich muss noch eine richtige Lösung dafür finden? Gibt es jemanden, der eine gute Lösung gefunden hat und erklärt, warum das funktioniert?
user1010819

Wie wäre es mit einer gut gemachten Bibliothek von Drittanbietern : SwipeBack ?
Devxoul

Antworten:


82

WICHTIG: Dies ist ein Hack. Ich würde empfehlen, sich diese Antwort anzuschauen .

Aufruf der folgenden Zeile nach Zuweisung der leftBarButtonItemfür mich bearbeiteten:

self.navigationController.interactivePopGestureRecognizer.delegate = self;

Bearbeiten: Dies funktioniert nicht, wenn initMethoden aufgerufen werden. Es sollte in viewDidLoadoder ähnlichen Methoden aufgerufen werden.


Stellen Sie dies auf dem View Controller ein? oder der Navigationscontroller? Ich habe das gleiche Problem, aber das scheint nicht zu funktionieren?
Kevin Renskers

1
@mixedCase Nach dem Herunterladen Ihres Beispielprojekts verstehe ich jetzt Ihr Problem. Der Code funktioniert so lange, wie der Inhalt der Sammlungsansicht die horizontale Breite des Ansichtscontrollers nicht überschreitet. Sobald die Sammlungsansicht jedoch horizontal scrollbar wird, überschreibt sie den interaktiven PopGestureRecognizer. Ich werde sehen, ob ich eine Lösung finden kann.
Paul Hunter

8
Ich habe ein Problem mit diesem Code. Es funktioniert nicht lange, nachdem VC 5-10 Back-Swipes eingefroren hat. Irgendeine Lösungsmöglichkeit?
Timur Bernikovich

1
Timur Bernikowich Ich habe das Gleiche erlebt. Hast du Gründe dafür gefunden?
user1010819

4
Festlegen, dass der Delegat selfihn von einem Klassenobjekt entfernt _UINavigationInteractiveTransition. Es liegt in der Verantwortung dieses Objekts, sicherzustellen, dass der Navigationscontroller nicht angewiesen wird, während des Übergangs zu platzen. Es wird noch untersucht, ob es möglich ist, diese Geste zu aktivieren, wenn die Zurück-Schaltfläche benutzerdefiniert ist.
Saltymule

56

Verwenden Sie nach Möglichkeit die Eigenschaften backIndicatorImage und backIndicatorTransitionMaskImage der UINavigationBar. Wenn Sie diese auf einem UIAppearanceProxy festlegen, können Sie das Verhalten in Ihrer Anwendung problemlos ändern. Die Falte ist, dass Sie diese nur auf ios 7 einstellen können, aber das funktioniert, weil Sie die Pop-Geste sowieso nur auf ios 7 verwenden können. Ihr normales ios 6-Styling kann intakt bleiben.

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
}

Der Versuch, den Delegierten des interaktiven PopGestureRecognizers zu manipulieren, führt zu vielen Problemen.


4
Danke Dan, das ist wirklich wichtig und sollte die akzeptierte Antwort sein. Indem Sie den Delegierten einfach neu zuweisen, öffnen Sie sich einer Menge seltsamer Verhaltensweisen. Besonders wenn Benutzer versuchen, auf das zurück zu wischen topViewController.
Chris Wagner

1
Dies ist eine Lösung, vorausgesetzt, Sie möchten lediglich das Bild der Zurück-Schaltfläche ändern. Was aber, wenn Sie den Text der Zurück-Schaltfläche und / oder die Aktion ändern möchten, die beim Klicken auf die Zurück-Schaltfläche ausgeführt wird?
user102008

An diesem Punkt sollten Sie das UINavigationItem.leftBarButtonItem besser festlegen. Es gibt eine Vielzahl von Antworten, die durch Suchen von leftBarButtonItem auf Google oder Stackoverflow gefunden werden können.
Saltymule

Wenn Sie die Änderung des Erscheinungsbilds auf Ihre eigene Benutzeroberfläche beschränken möchten und keine Auswirkungen auf Navigationsleisten haben möchten, die beispielsweise von ABPeoplePickerNavigationControllerIhnen erstellt wurden , können Sie eine benutzerdefinierte UINavigationControllerUnterklasse verwenden:[[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
Leider funktioniert dies unter iOS8 nicht. Die Schaltfläche "Zurück" ändert nichts an meinem benutzerdefinierten Bild.
Lensflare

29

Ich habe diese Lösung gesehen http://keighl.com/post/ios7-interactive-pop-gesture-custom-back-button/, die UINavigationController unterordnet. Dies ist eine bessere Lösung, da es den Fall behandelt, in dem Sie wischen, bevor der Controller installiert ist - was zu einem Absturz führt.

Außerdem ist mir aufgefallen, dass die Benutzeroberfläche nicht mehr reagiert, wenn Sie auf dem Root-View-Controller wischen (nachdem Sie einen gedrückt haben und wieder zurück) (auch das gleiche Problem in der obigen Antwort).

Der Code im untergeordneten UINavigationController sollte also folgendermaßen aussehen:

@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
hatte die gleichen Probleme beim Wischen auf dem Root View Controller, danke!
Michael Rose

DIE BESTE LÖSUNG AUF DER GANZEN SEITE. FUNKTIONIERT PERFEKT GUT. DANKE
Shahid Iqbal

19

ich benutze

[[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
Ich habe an anderer Stelle Kommentare gesehen, dass dies aufgrund eines Fehlers im Apple-Code derzeit Probleme im 64-Bit-Modus verursacht. Ich dachte nur, ich würde eine Warnung veröffentlichen
Peter Johnson,

@ PeterJohnson was für ein Fehler?
Art-Divin

Einfach die beste Lösung!
Igotit

Einfachste Lösung aller Zeiten.
tounaobun

6

Ich verstecke auch die Zurück-Schaltfläche und ersetze sie durch ein benutzerdefiniertes leftBarItem.
Das Entfernen des InteractivePopGestureRecognizer-Delegaten nach einer Push-Aktion hat bei mir funktioniert:

[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
Ein Problem bei dieser Methode besteht darin, dass die Benutzeroberfläche gesperrt wird, wenn Sie einen Kantenschwenk für eine Stammansicht ausführen. Ich bin darauf gestoßen, weil ich mehrere Instanzen derselben Ansicht auf den Navigationsstapel übertrage.
Nikola Lajic

@ MrNickBarker Danke, dass du mich informiert hast! Könnten Sie bitte das genaue Szenario beschreiben? Ich konnte es nicht reproduzieren, als ich den Root View Controller
wischte

4
Ich habe es in der viewDidLoad-Methode festgelegt. Meine endgültige Lösung bestand darin, eine andere Klasse als Delegaten festzulegen, die für 'gestureRecognizerShouldBegin' nur true zurückgibt, wenn sich mehr als ein Ansichts-Controller im Navigationsstapel befindet.
Nikola Lajic

MrNickBarker irgendwelche Gründe, warum es einfriert Ich erlebe das gleiche
user1010819

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

Dies ist von http://stuartkhall.com/posts/ios-7-development-tips-tricks-hacks , verursacht aber mehrere Fehler:

  1. Schieben Sie einen anderen viewController in den Navigationscontroller, wenn Sie vom linken Bildschirmrand nach innen wischen.
  2. Oder wischen Sie vom linken Bildschirmrand nach innen, wenn der topViewController vom Navigationscontroller angezeigt wird.

Wenn beispielsweise der rootViewController von navigationController angezeigt wird, wischen Sie vom linken Bildschirmrand nach innen und tippen Sie auf etwas (SCHNELL), um einen anderen ViewController in den Navigationscontroller zu verschieben

  • Der rootViewController reagiert nicht auf Berührungsereignisse.
  • Der otherViewController wird nicht angezeigt.
  • Wischen Sie erneut vom Bildschirmrand. Der andere ViewController wird angezeigt.
  • Tippen Sie auf die benutzerdefinierte Zurück-Schaltfläche, um den anderen ViewController zu öffnen. Absturz!

Sie müssen die UIGestureRecognizerDelegateMethode also folgendermaßen implementieren self.navigationController.interactivePopGestureRecognizer.delegate:

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

1
SwipeBack ist eine Lösung für diese Probleme.
Devxoul

6

Hier ist die swift3-Version der Antwort von Nick H247

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

Versuchen self.navigationController.interactivePopGestureRecognizer.enabled = YES;


Nein, es ist bereits aktiviert. Das Problem scheint zu sein, dass sein Delegat den Start der Gestenerkennung nicht zulässt. Ich habe meine Lösung hier als weitere Antwort hinzugefügt.
avishic

avishic könnten Sie bitte erklären, warum das Einstellen des Delegierten funktioniert? .. Ich bin mit dem gleichen Problem festgefahren.
user1010819

1

Ich habe dies nicht geschrieben, aber der folgende Blog hat mir sehr geholfen und meine Probleme mit der benutzerdefinierten Navigationsschaltfläche gelöst:

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

Zusammenfassend implementiert er einen benutzerdefinierten UINavigationController, der den Pop-Gesten-Delegaten verwendet. Sehr sauber und tragbar!

Code:

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

Bearbeiten. Korrektur für Probleme hinzugefügt, wenn ein Benutzer versucht, auf einem Root-View-Controller nach links zu wischen:

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

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

        return NO;
    }

    return YES;
}

Die Benutzeroberfläche friert ein, wenn Sie auf dem Root-View-Controller des Navigations-Controllers nach links wischen.
JohnVanDijk

@JohnVanDijk Ich habe die Antwort mit einem Fix bearbeitet, den ich meiner Meinung nach implementiert habe, um dieses Problem zu lösen. Ist schon eine Weile her, aber es macht Sinn. Wenn der Top View Controller der Root View Controller ist, werden wir
grundsätzlich

Es versteckt meinen Zurück-Knopf
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

Verwenden Sie diese Logik, um die Wischgeste weiterhin zu aktivieren oder zu deaktivieren.

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

Ich hatte ein ähnliches Problem, bei dem ich den aktuellen Ansichts-Controller als Delegaten für die interaktive Pop-Geste zuwies, die Geste jedoch bei allen Ansichten oder Ansichten unter der Ansicht im Navigationsstapel unterbrach. Die Art und Weise, wie ich das gelöst habe, bestand darin, den Delegaten -viewDidAppeareinzuschalten und ihn dann auf Null zu setzen -viewWillDisappear. Dadurch konnten meine anderen Ansichten korrekt funktionieren.


0

Stellen Sie sich vor, wir verwenden die Standard-Master- / Detailprojektvorlage von Apple, bei der master ein Controller für die Tabellenansicht ist. Wenn Sie darauf tippen, wird der Controller für die Detailansicht angezeigt.

Wir möchten die Schaltfläche "Zurück" anpassen, die im Detailansichts-Controller angezeigt wird. Auf diese Weise können Sie das Bild , die Bildfarbe , den Text , die Textfarbe und die Schriftart der Schaltfläche "Zurück" anpassen .


Um das Bild, die Bildfarbe, die Textfarbe oder die Schriftart global zu ändern, platzieren Sie Folgendes an einem Ort, der aufgerufen wird, bevor einer Ihrer Ansichts-Controller erstellt wird (z. B. application:didFinishLaunchingWithOptions:ein guter Ort).

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

Beachten Sie appearanceWhenContainedIn:, dass Sie mehr Kontrolle darüber haben können, welche Ansichtscontroller von diesen Änderungen betroffen sind. Beachten Sie jedoch, dass Sie nicht übergeben können[DetailViewController class] , da sie in einem UINavigationController und nicht in Ihrem DetailViewController enthalten sind. Dies bedeutet, dass Sie UINavigationController in Unterklassen unterteilen müssen, wenn Sie mehr Kontrolle darüber haben möchten, was betroffen ist.

Um den Text oder die Schriftart / Farbe eines bestimmten Elements der Zurück-Schaltfläche anzupassen, müssen Sie dies im MasterViewController tun (nicht im DetailViewController!). Dies scheint nicht intuitiv zu sein, da die Schaltfläche im DetailViewController angezeigt wird. Sobald Sie jedoch verstanden haben, dass das Anpassen durch Festlegen einer Eigenschaft in einem Navigationselement sinnvoller wird.

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

Hinweis: Der Versuch, die titleTextAttributes nach dem Festlegen von self.navigationItem.backBarButtonItem festzulegen, scheint nicht zu funktionieren. Sie müssen daher festgelegt werden, bevor Sie den Wert dieser Eigenschaft zuweisen.


0

Erstellen Sie eine Klasse 'TTNavigationViewController', die zur Unterklasse von 'UINavigationController' gehört, und erstellen Sie Ihren vorhandenen Navigationscontroller dieser Klasse entweder in Storyboard / Klasse, Beispielcode in Klasse -

    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
}}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.