Gezinme denetleyicisine bir görüntü aktardım ve geri düğmesine bastığımda otomatik olarak önceki görünüme gider. Görünümü yığından çıkarmadan önce geri düğmesine basıldığında birkaç şey yapmak istiyorum. Geri düğmesi geri arama işlevi hangisidir?
Gezinme denetleyicisine bir görüntü aktardım ve geri düğmesine bastığımda otomatik olarak önceki görünüme gider. Görünümü yığından çıkarmadan önce geri düğmesine basıldığında birkaç şey yapmak istiyorum. Geri düğmesi geri arama işlevi hangisidir?
Yanıtlar:
William Jockusch'un cevabı bu sorunu kolay bir hile ile çözer.
-(void) viewWillDisappear:(BOOL)animated {
if ([self.navigationController.viewControllers indexOfObject:self]==NSNotFound) {
// back button was pressed. We know this is true because self is no longer
// in the navigation stack.
}
[super viewWillDisappear:animated];
}
Bence en iyi çözüm.
- (void)didMoveToParentViewController:(UIViewController *)parent
{
if (![parent isEqual:self.parentViewController]) {
NSLog(@"Back pressed");
}
}
Ancak yalnızca iOS5 + ile çalışır
Kullanıcı onayı gibi şeyler için görünüm açılmadan önce olayı işleyebilmeniz için muhtemelen geri düğmesini geçersiz kılmak daha iyidir .
in viewDidLoad bir UIBarButtonItem oluşturun ve self.navigationItem.leftBarButtonItem öğesini bir sel içinde geçirerek ayarlayın
- (void) viewDidLoad
{
// change the back button to cancel and add an event handler
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:@”back”
style:UIBarButtonItemStyleBordered
target:self
action:@selector(handleBack:)];
self.navigationItem.leftBarButtonItem = backButton;
[backButton release];
}
- (void) handleBack:(id)sender
{
// pop to root view controller
[self.navigationController popToRootViewControllerAnimated:YES];
}
Ardından, eylemi onaylamak için bir UIAlertView yükseltmek, ardından görünüm denetleyicisini açmak gibi şeyler yapabilirsiniz.
Veya yeni bir geri düğmesi oluşturmak yerine, geri düğmesine basıldığında eylemler yapmak için UINavigationController delege yöntemlerine uyabilirsiniz.
UINavigationControllerDelegate
Geriye dönüş düğmesi dokunulduğunda denir yöntemi yok.
Bu çözümlerle sonuçlandım. Geri düğmesine dokunduğumuzda viewDidDisappear yöntemi çağrılır. true döndüren isMovingFromParentViewController seçicisini çağırarak kontrol edebiliriz. verileri geri verebiliriz (Delege Kullanarak). umarım bu birisine yardımcı olur.
-(void)viewDidDisappear:(BOOL)animated{
if (self.isMovingToParentViewController) {
}
if (self.isMovingFromParentViewController) {
//moving back
//pass to viewCollection delegate and update UI
[self.delegateObject passBackSavedData:self.dataModel];
}
}
[super viewDidDisappear:animated]
Belki biraz geç, ama daha önce de aynı davranışı istedim. Ve benim kullandığım çözüm şu anda App Store'da bulunan uygulamalardan birinde oldukça iyi çalışıyor. Kimsenin benzer bir yöntemle gittiğini görmediğim için burada paylaşmak istiyorum. Bu çözümün dezavantajı, alt sınıflandırma gerektirmesidir UINavigationController
. Yöntem Swizzling kullanmasına rağmen kaçınmaya yardımcı da, o kadar ileri gitmedim.
Yani, varsayılan geri düğmesi aslında tarafından yönetilir UINavigationBar
. Bir kullanıcı geri düğmesine dokunduğunda UINavigationBar
, temsilcisine UINavigationItem
arayarak yukarı çıkıp çıkmayacağını sorun navigationBar(_:shouldPop:)
. UINavigationController
bunu gerçekte uygular, ancak benimsediğini kamuya açıklamaz UINavigationBarDelegate
(neden !?). Bu olayı engellemek için bir alt sınıf oluşturun UINavigationController
, uygunluğunu bildirin UINavigationBarDelegate
ve uygulayın navigationBar(_:shouldPop:)
. true
En üstteki öğenin çıkarılması gerekiyorsa geri dönün . Kalması false
gerekiyorsa geri dön .
İki sorun var. Birincisi , bir noktada UINavigationController
versiyonunu aramanız gerektiğidir navigationBar(_:shouldPop:)
. Ancak UINavigationBarController
kamuya açık bir şekilde uygunluğunu beyan etmezUINavigationBarDelegate
, çağırmaya çalışmak derleme zamanı hatasıyla sonuçlanır. Benim kullandığım çözüm, uygulamayı doğrudan almak ve çağırmak için Objective-C çalışma zamanını kullanmak. Daha iyi bir çözümü olan varsa lütfen bana bildirin.
Diğer sorun ise, navigationBar(_:shouldPop:)
ilk önce popViewController(animated:)
kullanıcı geri düğmesine dokunursa takip eder. Görünüm denetleyicisi çağrılarak açılırsa sıra tersine döner popViewController(animated:)
. Bu durumda, popViewController(animated:)
daha önce çağrılıp çağrılmadığını tespit etmek için bir boole kullanıyorum , navigationBar(_:shouldPop:)
bu da kullanıcının geri düğmesine dokunduğu anlamına geliyor.
Ayrıca, UIViewController
gezinme denetleyicisinin görünüm denetleyicisine, kullanıcı geri düğmesine dokunduğunda açılıp açılmayacağını sormasına izin veren bir uzantı oluşturuyorum . Denetleyicileri görüntüle geri dönüp false
gerekli eylemleri yapabilir ve popViewController(animated:)
daha sonra arayabilir .
class InterceptableNavigationController: UINavigationController, UINavigationBarDelegate {
// If a view controller is popped by tapping on the back button, `navigationBar(_:, shouldPop:)` is called first follows by `popViewController(animated:)`.
// If it is popped by calling to `popViewController(animated:)`, the order reverses and we need this flag to check that.
private var didCallPopViewController = false
override func popViewController(animated: Bool) -> UIViewController? {
didCallPopViewController = true
return super.popViewController(animated: animated)
}
func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
// If this is a subsequence call after `popViewController(animated:)`, we should just pop the view controller right away.
if didCallPopViewController {
return originalImplementationOfNavigationBar(navigationBar, shouldPop: item)
}
// The following code is called only when the user taps on the back button.
guard let vc = topViewController, item == vc.navigationItem else {
return false
}
if vc.shouldBePopped(self) {
return originalImplementationOfNavigationBar(navigationBar, shouldPop: item)
} else {
return false
}
}
func navigationBar(_ navigationBar: UINavigationBar, didPop item: UINavigationItem) {
didCallPopViewController = false
}
/// Since `UINavigationController` doesn't publicly declare its conformance to `UINavigationBarDelegate`,
/// trying to called `navigationBar(_:shouldPop:)` will result in a compile error.
/// So, we'll have to use Objective-C runtime to directly get super's implementation of `navigationBar(_:shouldPop:)` and call it.
private func originalImplementationOfNavigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
let sel = #selector(UINavigationBarDelegate.navigationBar(_:shouldPop:))
let imp = class_getMethodImplementation(class_getSuperclass(InterceptableNavigationController.self), sel)
typealias ShouldPopFunction = @convention(c) (AnyObject, Selector, UINavigationBar, UINavigationItem) -> Bool
let shouldPop = unsafeBitCast(imp, to: ShouldPopFunction.self)
return shouldPop(self, sel, navigationBar, item)
}
}
extension UIViewController {
@objc func shouldBePopped(_ navigationController: UINavigationController) -> Bool {
return true
}
}
Ve sizde kontrolörleri görüntüleyin, uygulayın shouldBePopped(_:)
. Bu yöntemi uygulamazsanız, varsayılan davranış, kullanıcı geri düğmesine normal gibi dokunur dokunmaz görünüm denetleyicisini açmak olacaktır.
class MyViewController: UIViewController {
override func shouldBePopped(_ navigationController: UINavigationController) -> Bool {
let alert = UIAlertController(title: "Do you want to go back?",
message: "Do you really want to go back? Tap on \"Yes\" to go back. Tap on \"No\" to stay on this screen.",
preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "No", style: .cancel, handler: nil))
alert.addAction(UIAlertAction(title: "Yes", style: .default, handler: { _ in
navigationController.popViewController(animated: true)
}))
present(alert, animated: true, completion: nil)
return false
}
}
Demomu buradan inceleyebilirsiniz .
"Görünümü yığından çıkarmadan ÖNCE" için:
- (void)willMoveToParentViewController:(UIViewController *)parent{
if (parent == nil){
NSLog(@"do whatever you want here");
}
}
ViewControllers'a sormaktan daha uygun bir yol var. Oyun kumandanızı, geri düğmesi olan bir gezinti çubuğu temsilcisi yapabilirsiniz. İşte bir örnek. Geri düğmesine basmak istediğiniz denetleyicinin uygulamasında, UINavigationBarDelegate protokolünü uygulayacağını söyleyin:
@interface MyViewController () <UINavigationBarDelegate>
Ardından başlatma kodunuzda bir yerde (muhtemelen viewDidLoad'da) denetleyicinizi gezinme çubuğunun temsilcisi yapın:
self.navigationController.navigationBar.delegate = self;
Son olarak, shouldPopItem yöntemini uygulayın. Bu yöntem hemen geri düğmesine basıldığında çağrılır. Yığın içinde birden fazla denetleyiciniz veya gezinme Öğeniz varsa, muhtemelen bu gezinme öğelerinden hangisinin patladığını (öğe parametresi) kontrol etmek isteyeceksiniz, böylece yalnızca istediğiniz zaman özel işlerinizi yaparsınız. İşte bir örnek:
-(BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item
{
NSLog(@"Back button got pressed!");
//if you return NO, the back button press is cancelled
return YES;
}
"ViewWillDisappear" veya benzer bir yöntemi kullanamıyorsanız, UINavigationController alt sınıfını deneyin. Bu başlık sınıfıdır:
#import <Foundation/Foundation.h>
@class MyViewController;
@interface CCNavigationController : UINavigationController
@property (nonatomic, strong) MyViewController *viewController;
@end
Uygulama sınıfı:
#import "CCNavigationController.h"
#import "MyViewController.h"
@implementation CCNavigationController {
}
- (UIViewController *)popViewControllerAnimated:(BOOL)animated {
@"This is the moment for you to do whatever you want"
[self.viewController doCustomMethod];
return [super popViewControllerAnimated:animated];
}
@end
Öte yandan, bu viewController'ı özel NavigationController'ınıza bağlamanız gerekir, bu nedenle, normal viewController'ınız için viewDidLoad yönteminizde şunu yapın:
@implementation MyViewController {
- (void)viewDidLoad
{
[super viewDidLoad];
((CCNavigationController*)self.navigationController).viewController = self;
}
}
İşte benim uyguladığım başka bir yol (çözülme segmentiyle test etmedim, ancak başkalarının bu sayfadaki diğer çözümlerle ilgili olarak belirttiği gibi muhtemelen farklılaşmayacaktır) üst görünüm denetleyicisinin, ittiği alt VC'den önce eylemler gerçekleştirmesini sağlamak görünüm yığınından atılır (bunu orijinal UINavigationController'dan birkaç seviye aşağıda kullandım). Bu aynı zamanda childVC'ye itilmeden önce eylemler gerçekleştirmek için de kullanılabilir. Bu, özel bir UIBarButtonItem veya UIButton oluşturmak zorunda kalmadan, iOS sistemi geri düğmesiyle çalışmanın ek avantajına sahiptir.
Ana VC'nizin UINavigationControllerDelegate
protokolü benimsemesini ve delege mesajları için kaydolmasını sağlayın :
MyParentViewController : UIViewController <UINavigationControllerDelegate>
-(void)viewDidLoad {
self.navigationcontroller.delegate = self;
}
Bu UINavigationControllerDelegate
örnek yöntemini şuraya uygulayın MyParentViewController
:
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC {
// Test if operation is a pop; can also test for a push (i.e., do something before the ChildVC is pushed
if (operation == UINavigationControllerOperationPop) {
// Make sure it's the child class you're looking for
if ([fromVC isKindOfClass:[ChildViewController class]]) {
// Can handle logic here or send to another method; can also access all properties of child VC at this time
return [self didPressBackButtonOnChildViewControllerVC:fromVC];
}
}
// If you don't want to specify a nav controller transition
return nil;
}
Yukarıdaki UINavigationControllerDelegate
örnek yönteminde belirli bir geri çağırma işlevi belirtirseniz
-(id <UIViewControllerAnimatedTransitioning>)didPressBackButtonOnAddSearchRegionsVC:(UIViewController *)fromVC {
ChildViewController *childVC = ChildViewController.new;
childVC = (ChildViewController *)fromVC;
// childVC.propertiesIWantToAccess go here
// If you don't want to specify a nav controller transition
return nil;
}