Mam aplikację na iOS 7, w której ustawiam niestandardowy przycisk Wstecz w następujący 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” w iOS 7, aby przejść do poprzedniego kontrolera. Czy ktoś wie, jak mogę ustawić niestandardowy przycisk i nadal mieć ten gest włączony?
EDYCJA: Próbowałem zamiast tego ustawić viewController.navigationItem.backBarButtonItem, ale nie wydaje się, aby wyświetlał mój niestandardowy obraz.
Odpowiedzi:
WAŻNE: to jest hack. Poleciłbym przyjrzeć się tej odpowiedzi .
Dzwonię pod następujący numer po przypisaniu
leftBarButtonItem
mi działał:self.navigationController.interactivePopGestureRecognizer.delegate = self;
Edycja: to nie działa, jeśli wywoływane są
init
metody. Powinien zostać wywołanyviewDidLoad
lub podobnymi metodami.źródło
self
usunięcia go z obiektu klasy_UINavigationInteractiveTransition
. Obowiązkiem tego obiektu jest upewnienie się, że kontroler nawigacji nie zostanie poproszony o wyskakiwanie podczas przejścia. Wciąż badam, czy można włączyć ten gest, czy nie, gdy przycisk Wstecz jest niestandardowy.Jeśli to możliwe, użyj właściwości backIndicatorImage i backIndicatorTransitionMaskImage elementu UINavigationBar. Ustawienie ich na UIAppearanceProxy może łatwo modyfikować zachowanie w całej aplikacji. Problem polega na tym, że możesz ustawić je tylko na ios 7, ale to działa, ponieważ i tak możesz używać tylko gestu pop na ios 7. 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.
źródło
topViewController
.ABPeoplePickerNavigationController
Ciebie, możesz użyć niestandardowejUINavigationController
podklasy:[[UINavigationBar appearanceWhenContainedIn:[THNavigationController class], nil] setBackIndicatorImage:[UIImage imageNamed:@"btn_back_arrow"]];
[[UINavigationBar appearanceWhenContainedIn:[THNavigationController class], nil] setBackIndicatorTransitionMaskImage:[UIImage imageNamed:@"btn_back_arrow_highlighted"]];
Widziałem to rozwiązanie http://keighl.com/post/ios7-interactive-pop-gesture-custom-back-button/, które podklasy UINavigationController. Jest to lepsze rozwiązanie, ponieważ obsługuje przypadek, w którym przesuwasz palcem, zanim kontroler jest na miejscu - co powoduje awarię.
Oprócz tego zauważyłem, że jeśli wykonasz przesunięcie palcem na głównym kontrolerze widoku (po naciśnięciu jednego i z powrotem) interfejs użytkownika przestaje odpowiadać (również ten sam problem w odpowiedzi powyżej).
Więc kod w podklasie 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
źródło
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];
źródło
Ukrywam też przycisk Wstecz, zastępując go niestandardowym leftBarItem.
Usunięcie delegata InteractivePopGestureRecognizer po akcji wypychania działało u 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; }
źródło
navigationController.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)self;
To pochodzi z http://stuartkhall.com/posts/ios-7-development-tips-tricks-hacks , ale powoduje kilka błędów:
np. gdy wyświetla się rootViewController z navigationController, przesuń palcem od lewej krawędzi ekranu i dotknij czegoś (SZYBKO), aby wypchnąć anotherViewController do navigationController, a następnie
Więc musisz zaimplementować
UIGestureRecognizerDelegate
metodę w następujący sposóbself.navigationController.interactivePopGestureRecognizer.delegate
:- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { if (gestureRecognizer == navigationController.interactivePopGestureRecognizer) { return !navigationController.<#TODO: isPushAnimating#> && [navigationController.viewControllers count] > 1; } return YES; }
źródło
Oto szybka wersja odpowiedzi Nicka 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 {}
źródło
Próbować
self.navigationController.
interactivePopGestureRecognizer
.enabled = YES;
źródło
Nie napisałem tego, ale poniższy blog bardzo pomógł i rozwiązał moje problemy z niestandardowym przyciskiem nawigacji:
http://keighl.com/post/ios7-interactive-pop-gesture-custom-back-button/
Podsumowując, implementuje niestandardowy kontroler UINavigationController, który używa delegata gestu 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; }
Edytować. Dodano naprawę problemów, gdy użytkownik próbuje przesunąć palcem w lewo na głównym kontrolerze widoku:
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)] && self.topViewController == [self.viewControllers firstObject] && gestureRecognizer == self.interactivePopGestureRecognizer) { return NO; } return YES; }
źródło
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 {}
źródło
Użyj tej logiki, aby włączyć lub wyłączyć gest przesunięcia.
- (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; } } }
źródło
Miałem podobny problem, gdy przypisywałem bieżący kontroler widoku jako delegata dla interaktywnego gestu pop, ale przerywałem ten gest w przypadku wszystkich wypchniętych widoków lub widoków pod widokiem w stosie nawigacji. Sposób, w jaki to rozwiązałem, polegał na ustawieniu delegata
-viewDidAppear
, a następnie ustawieniu go na zero-viewWillDisappear
. To pozwoliło moim innym widokom działać poprawnie.źródło
Wyobraź sobie, że używamy domyślnego szablonu projektu głównego / szczegółów firmy Apple, w którym wzorzec jest kontrolerem widoku tabeli, a dotknięcie go spowoduje wyświetlenie kontrolera widoku szczegółów.
Chcemy dostosować przycisk Wstecz, który pojawia się w kontrolerze widoku szczegółów. Oto jak dostosować obraz , kolor obrazu , tekst , kolor tekstu i czcionkę przycisku Wstecz.
Aby globalnie zmienić obraz, kolor obrazu, kolor tekstu lub czcionkę, umieść następujące elementy w lokalizacji, która jest wywoływana przed utworzeniem któregokolwiek z kontrolerów widoku (np.
application:didFinishLaunchingWithOptions:
Jest to dobre miejsce).- (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 kontrolerami widoku, na które wpływają te zmiany, ale pamiętaj, że nie możesz ich przekazać[DetailViewController class]
, ponieważ jest zawarty w UINavigationController, a nie w Twoim DetailViewController. Oznacza to, że jeśli chcesz mieć większą kontrolę nad tym, czego dotyczy problem, musisz utworzyć podklasę UINavigationController.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ę na DetailViewController. Jednak gdy zrozumiesz, że sposobem dostosowywania jest ustawienie właściwości w navigationItem, zaczyna to mieć więcej sensu.
- (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 atrybutu titleTextAttributes po ustawieniu self.navigationItem.backBarButtonItem nie wydaje się działać, dlatego należy je ustawić przed przypisaniem wartości do tej właściwości.
źródło
Utwórz klasę „TTNavigationViewController”, która jest podklasą „UINavigationController”, i utwórz istniejący kontroler nawigacji tej klasy w scenorysie / klasie, przykładowy kod w klasie -
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 }}
źródło