Gezinti denetleyicisindeki geri düğmesi için eylem ayarlama


180

Gezinti denetleyicisindeki geri düğmesinin varsayılan eyleminin üzerine yazmaya çalışıyorum. Özel düğmede bir hedef belirledim. Garip şey, backbutton özelliği olsa da atamalarına dikkat etmiyor ve sadece geçerli görünümü açıyor ve köke geri dönüyor:

UIBarButtonItem *backButton = [[UIBarButtonItem alloc] 
                                  initWithTitle: @"Servers" 
                                  style:UIBarButtonItemStylePlain 
                                  target:self 
                                  action:@selector(home)];
self.navigationItem.backBarButtonItem = backButton;

leftBarButtonItemÜzerinde ayarladığım anda navigationItemeylemimi çağırıyor, ancak düğme geri oklu yerine düz bir yuvarlak gibi görünüyor:

self.navigationItem.leftBarButtonItem = backButton;

Kök görünümüne dönmeden önce özel işlemimi çağırmasını nasıl sağlayabilirim? Varsayılan geri eylemin üzerine yazmanın bir yolu var mı veya bir görünümden ayrılırken her zaman çağrılan bir yöntem var viewDidUnloadmı ( bunu yapmıyor)?


Eylem: @selector (ana)]; a: seçici eyleminden sonra: @selector (home :)]; aksi takdirde çalışmaz
PartySoft

7
@PartySoft Yöntem iki nokta üst üste ile bildirilmediği sürece bu doğru değildir. Herhangi bir parametre almayan düğmelerin çağrı seçicileri olması mükemmel şekilde geçerlidir.
mbm29414


3
Apple neden geri düğmesi gibi şekillendirilmiş bir düğme sunmuyor? Oldukça açık görünüyor.
JohnK

Yanıtlar:


363

Bunu, baskı makinesini algılamak istediğiniz görüntü denetleyicisine yerleştirmeyi deneyin:

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

1
Bu kaygan, temiz, güzel ve çok iyi düşünülmüş bir çözüm
boliva

6
+1 büyük hack, ancak pop animasyonu üzerinde kontrol sunmuyor
matm

3
Temsilciye bir düğme aracılığıyla bir mesaj gönderirsem ve temsilci denetleyiciyi açarsa benim için çalışmıyor - bu yine de devam ediyor.
SAHM

21
Başka bir sorun, kullanıcı geri düğmesine basarsa veya programlı olarak [self.navigationController popViewControllerAnimated: YES]
Chase Roberts

10
Sadece bir FYI: Hızlı sürüm:if (find(self.navigationController!.viewControllers as! [UIViewController],self)==nil)
hEADcRASH

177

UIViewController-BackButtonHandler uzantısını uyguladım . Hiçbir şeyi alt sınıflamaya gerek yoktur, sadece projenize koyun ve sınıftaki navigationShouldPopOnBackButtonyöntemi geçersiz kılın UIViewController:

-(BOOL) navigationShouldPopOnBackButton {
    if(needsShowConfirmation) {
        // Show confirmation alert
        // ...
        return NO; // Ignore 'Back' button this time
    }
    return YES; // Process 'Back' button click and Pop view controler
}

Örnek uygulamayı indirin .


16
Bu gördüğüm en temiz çözüm, kendi özel UIButton'unuzu kullanmaktan daha iyi ve daha basit. Teşekkürler!
13'te ramirogm

4
navigationBar:shouldPopItem:Bunun bir parçası olduğundan özel bir yöntem değildir UINavigationBarDelegateprotokolü.
onegray

1
ama UINavigationController temsilci zaten (geri dönmek için YES) uyguluyor mu? yoksa gelecekte olacak mı? alt sınıf muhtemelen daha güvenli bir seçenektir
Sam

8
Bunu (7.1'de oldukça güzel BTW) uyguladım NOve geri düğmesini döndükten sonra devre dışı durumda kaldığını fark ettim (görsel olarak, dokunma olaylarına hala alıp tepki verdiğinden). Gezinme çubuğu alt görünümleri aracılığıyla kontrole bir elseaçıklama ekleyerek shouldPopve bisiklete binerek ve alphabir animasyon bloğu içinde gerekirse değeri 1'e ayarlayarak etrafında
çalıştım

2
Bu, şimdiye kadar gördüğüm en iyi uzantılardan biri. Çok teşekkür ederim.
Srikanth

42

Amagrammer'ın aksine, bu mümkün. Alt sınıfınızı oluşturmanız gerekir navigationController. Burada her şeyi açıkladım (örnek kod dahil).


Apple'ın belgeleri ( developer.apple.com/iphone/library/documentation/UIKit/… ) "Bu sınıf alt sınıflandırma için tasarlanmamıştır" diyor. Bununla ne anlama geldiğinden emin olmasam da - "normalde bunu yapmanıza gerek yok" anlamına gelebilir veya "kontrolörümüzle
uğraşırsanız

Bunu yapmanın tek yolu bu. Sana daha fazla puan verebilseydim Hans!
Adam Eberbach

1
Bu yöntemi kullanarak bir görünümün çıkmasını gerçekten engelleyebilir misiniz? Görünümün çıkmamasını istiyorsanız popViewControllerAnimated yönteminin döndürülmesini ne yaparsınız?
JosephH

1
Evet yapabilirsin. Sadece uygulamanızda üst sınıf yöntemini çağırma, unutmayın! Bunu yapmamalısınız, kullanıcı navigasyona geri dönmeyi bekler. Yapabileceğiniz şey bir onay istemektir. Apples belgelerine göre popViewController şunu döndürür: "Yığından atılan görünüm denetleyicisi." Yani hiçbir şey patladığında nil dönmelidir;
HansPinckaers

1
@HansPickaers Bence bir görüşün çıkmasını engelleme konusundaki cevabınız biraz yanlış olabilir. PopViewControllerAnimated: alt sınıfları uygulamasından bir 'onayla' mesajı görüntülersem, NavigationBar döndürdüğümden bağımsız olarak yine de ağaçta bir seviye yukarı hareket ettirir. Bunun nedeni, geri düğmesine tıklamanın gezinme çubuğunda shouldPopNavigationItem'i çağırmasıdır. Tavsiye olarak benim alt sınıflar yönteminden nil iade ediyorum.
deepwinter

15

Hızlı Sürüm:

( https://stackoverflow.com/a/19132881/826435 )

Görünüm denetleyicinizde bir protokole uymanız ve ihtiyacınız olan tüm eylemleri gerçekleştirmeniz gerekir:

extension MyViewController: NavigationControllerBackButtonDelegate {
    func shouldPopOnBackButtonPress() -> Bool {
        performSomeActionOnThePressOfABackButton()
        return false
    }
}

Sonra bir sınıf oluşturun, diyelim NavigationController+BackButtonve aşağıdaki kodu kopyalayıp yapıştırın:

protocol NavigationControllerBackButtonDelegate {
    func shouldPopOnBackButtonPress() -> Bool
}

extension UINavigationController {
    public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
        // Prevents from a synchronization issue of popping too many navigation items
        // and not enough view controllers or viceversa from unusual tapping
        if viewControllers.count < navigationBar.items!.count {
            return true
        }

        // Check if we have a view controller that wants to respond to being popped
        var shouldPop = true
        if let viewController = topViewController as? NavigationControllerBackButtonDelegate {
            shouldPop = viewController.shouldPopOnBackButtonPress()
        }

        if (shouldPop) {
            DispatchQueue.main.async {
                self.popViewController(animated: true)
            }
        } else {
            // Prevent the back button from staying in an disabled state
            for view in navigationBar.subviews {
                if view.alpha < 1.0 {
                    UIView.animate(withDuration: 0.25, animations: {
                        view.alpha = 1.0
                    })
                }
            }

        }

        return false
    }
}

Belki bir şey kaçırdım, ama benim için işe yaramaz, yöntem performSomeActionOnThePressOfABackButton uzantının adı asla çağrılmaz
Turvy

@FlorentBreton belki bir yanlış anlama? shouldPopOnBackButtonPresshiçbir hata olmadığı sürece çağrılmalıdır. performSomeActionOnThePressOfABackButtonsadece var olmayan bir telafi yöntemidir.
kgaidis

Anladım, bu yüzden performSomeActionOnThePressOfABackButtondenetleyicimde geri düğmesine basıldığında belirli bir eylemi yürütmek için bir yöntem oluşturdum , ancak bu yöntem asla çağrılmadı, eylem normal bir geri dönüş
Turvy

1
Benim için de çalışmıyor. ShouldPop yöntemi hiçbir zaman çağrılmaz. Bir yere bir delege mi koydun?
Tim Autin

@TimAutin Bunu tekrar test ettim ve bir şeyler değişmiş gibi görünüyor. Anahtar kısmı içinde olduğunu anlamak UINavigationController, navigationBar.delegatenavigasyon kontrolörü olarak ayarlanır. Yani yöntemler çağrılmalıdır. Ancak Swift'te, bir alt sınıfta bile çağrılmaları mümkün değil. Bununla birlikte, Objective-C'de çağrılmalarını sağladım, bu yüzden şimdilik Objective-C sürümünü kullanacağım. Swift hatası olabilir.
kgaidis

5

Doğrudan yapmak mümkün değil. Birkaç alternatif var:

  1. UIBarButtonItemTest başarılı olursa dokunarak doğrulayan ve açılan kendi özelinizi oluşturun
  2. Form alanı içeriklerini , klavyede veya düğmesine basıldıktan sonra çağrılan bir UITextFieldtemsilci yöntemi kullanarak doğrulayın.-textFieldShouldReturn:ReturnDone

İlk seçeneğin dezavantajı, geri düğmesinin sol ok işaret stiline özel bir çubuk düğmesinden erişilememesidir. Bu yüzden bir görüntü kullanmanız veya normal bir stil düğmesiyle gitmeniz gerekiyor.

İkinci seçenek güzeldir, çünkü metin alanını temsilci yönteminde geri alırsınız, böylece doğrulama mantığınızı temsilci geri arama yöntemine gönderilen belirli metin alanına hedefleyebilirsiniz.


5

Bazı tehdit edici nedenlerden dolayı, @HansPinckaers'ın bahsettiği çözüm benim için doğru değildi, ancak arka düğmeye dokunmanın çok daha kolay bir yolunu buldum ve bunun saatlerce aldatmacayı önleyebilmesi için bunu buraya sabitlemek istiyorum başkası. Hile gerçekten kolaydır: UINavigationBar'ınıza alt görünüm olarak şeffaf bir UIButton ekleyin ve seçicilerinizi gerçek düğmemiş gibi ayarlayın! İşte Monotouch ve C # kullanan bir örnek, ancak objektif-c'ye çeviriyi bulmak çok zor olmamalı.

public class Test : UIViewController {
    public override void ViewDidLoad() {
        UIButton b = new UIButton(new RectangleF(0, 0, 60, 44)); //width must be adapted to label contained in button
        b.BackgroundColor = UIColor.Clear; //making the background invisible
        b.Title = string.Empty; // and no need to write anything
        b.TouchDown += delegate {
            Console.WriteLine("caught!");
            if (true) // check what you want here
                NavigationController.PopViewControllerAnimated(true); // and then we pop if we want
        };
        NavigationController.NavigationBar.AddSubview(button); // insert the button to the nav bar
    }
}

Eğlenceli gerçek: test amaçlı ve sahte düğmem için iyi boyutlar bulmak için arka plan rengini maviye ayarladım ... Ve arka düğmenin arkasında görünüyor ! Her neyse, orijinal düğmeyi hedefleyen herhangi bir dokunuşu yakalar.


3

Bu teknik, herhangi bir görünüm denetleyicisinin başlığını etkilemeden veya animasyon sırasında geri düğmesi metninin değiştiğini görmeden "geri" düğmesinin metnini değiştirmenize olanak tanır.

Bunu, çağıran görünüm denetleyicisindeki init yöntemine ekleyin :

UIBarButtonItem *temporaryBarButtonItem = [[UIBarButtonItem alloc] init];   
temporaryBarButtonItem.title = @"Back";
self.navigationItem.backBarButtonItem = temporaryBarButtonItem;
[temporaryBarButtonItem release];

3

En kolay yol

UINavigationController'ın temsilci yöntemlerini kullanabilirsiniz. Yöntem willShowViewController, VC'nizin geri düğmesine basıldığında çağrılır. Geri btn'ye basıldığında ne istersen yap

- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated;

Görünüm denetleyicinizin kendisini devralınan navigationController delegesi olarak ayarladığından ve UINavigationControllerDelegate protokolüne uyduğundan emin olun
Justin Milo

3

İşte benim Swift çözümüm. UIViewController alt sınıfınızda, navigationShouldPopOnBackButton yöntemini geçersiz kılın.

extension UIViewController {
    func navigationShouldPopOnBackButton() -> Bool {
        return true
    }
}

extension UINavigationController {

    func navigationBar(navigationBar: UINavigationBar, shouldPopItem item: UINavigationItem) -> Bool {
        if let vc = self.topViewController {
            if vc.navigationShouldPopOnBackButton() {
                self.popViewControllerAnimated(true)
            } else {
                for it in navigationBar.subviews {
                    let view = it as! UIView
                    if view.alpha < 1.0 {
                        [UIView .animateWithDuration(0.25, animations: { () -> Void in
                            view.alpha = 1.0
                        })]
                    }
                }
                return false
            }
        }
        return true
    }

}

UIViewController'daki geçersiz kılma yöntemi navigationShouldPopOnBackButton çalışmıyor - Program, geçersiz kılınan yöntemi değil üst yöntemi çalıştırır. Bunun için bir çözüm var mı? Herkesin aynı sorunu var mı?
Pawel Cala

dönüş doğru ise rootview'e geri döner
Pawriwes

@Pawriwes İşte benim için işe
yaramış

3

Geri düğmesi stilini de koruyan bir çözüm bulundu. Görünüm denetleyicinize aşağıdaki yöntemi ekleyin.

-(void) overrideBack{

    UIButton *transparentButton = [[UIButton alloc] init];
    [transparentButton setFrame:CGRectMake(0,0, 50, 40)];
    [transparentButton setBackgroundColor:[UIColor clearColor]];
    [transparentButton addTarget:self action:@selector(backAction:) forControlEvents:UIControlEventTouchUpInside];
    [self.navigationController.navigationBar addSubview:transparentButton];


}

Şimdi aşağıdaki yöntemde gerektiği gibi bir işlevsellik sağlayın:

-(void)backAction:(UIBarButtonItem *)sender {
    //Your functionality
}

Tek yaptığı geri düğmesini şeffaf bir düğme ile örtmek;)


3

NavigationBar geçersiz kılma (_ NavigationBar: shouldPop) : Bu değil çalıştığını bile iyi bir fikir. benim için geriye dönerek rastgele çökmelere neden oldu. NavigationItem öğesinden varsayılan backButton öğesini kaldırarak ve aşağıdaki gibi özel bir geri düğmesi oluşturarak geri düğmesini geçersiz kılmanızı öneririm:

override func viewDidLoad(){
   super.viewDidLoad()
   
   navigationItem.leftBarButton = .init(title: "Go Back", ... , action: #selector(myCutsomBackAction) 

   ...
 
}

========================================

Önceki cevaplara Bina UIAlert içinde Swift5 bir de uyumsuz bir şekilde


protocol NavigationControllerBackButtonDelegate {
    func shouldPopOnBackButtonPress(_ completion: @escaping (Bool) -> ())
}

extension UINavigationController: UINavigationBarDelegate {
    public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
      
        if viewControllers.count < navigationBar.items!.count {
            return true
        }
        
        // Check if we have a view controller that wants to respond to being popped
        
        if let viewController = topViewController as? NavigationControllerBackButtonDelegate {
            
            viewController.shouldPopOnBackButtonPress { shouldPop in
                if (shouldPop) {
                    /// on confirm => pop
                    DispatchQueue.main.async {
                        self.popViewController(animated: true)
                    }
                } else {
                    /// on cancel => do nothing
                }
            }
            /// return false => so navigator will cancel the popBack
            /// until user confirm or cancel
            return false
        }else{
            DispatchQueue.main.async {
                self.popViewController(animated: true)
            }
        }
        return true
    }
}

Kumandanızda


extension MyController: NavigationControllerBackButtonDelegate {
    
    func shouldPopOnBackButtonPress(_ completion: @escaping (Bool) -> ()) {
    
        let msg = "message"
        
        /// show UIAlert
        alertAttention(msg: msg, actions: [
            
            .init(title: "Continuer", style: .destructive, handler: { _ in
                completion(true)
            }),
            .init(title: "Annuler", style: .cancel, handler: { _ in
                completion(false)
            })
            ])
   
    }

}

İf viewControllers.count <navigationBar.items! .Count {return true} onayında neler olup bittiği hakkında ayrıntılı bilgi verebilir misiniz?
H4Hugo

// Çok fazla gezinme öğesi
patlattığında

2

Bunun mümkün olduğuna kolayca inanmıyorum. Bunu aşmanın tek yolu, kendi geri düğmesi ok görüntünüzü oraya yerleştirmektir. İlk başta benim için sinir bozucuydu ama neden tutarlılık uğruna dışlandığını anlıyorum.

Normal bir düğme oluşturarak ve varsayılan geri düğmesini gizleyerek (ok olmadan) yaklaşabilirsiniz:

self.navigationItem.leftBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:@"Servers" style:UIBarButtonItemStyleDone target:nil action:nil] autorelease];
self.navigationItem.hidesBackButton = YES;

2
Evet sorun, normal geri düğmesi gibi görünmesini istiyorum, sadece önce özel eylemimi çağırması gerekiyor ...
Papağanlar

2

Sadece sınıflara göre daha kolay bir yolu var temsilci yöntemini ait UINavigationBarve geçersiz ShouldPopItemyöntemi .


Ben alt sınıf UINavigationController sınıfı demek ve bir shouldPopItem yöntemi uygulamak demek düşünüyorum. Bu benim için iyi çalışıyor. Ancak, bu yöntem beklediğiniz gibi YES veya NO döndürmemelidir. Bir açıklama ve çözüm burada bulunabilir: stackoverflow.com/a/7453933/462162
arlomedia

2

Apple'ın resmi belgelerine göre, https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html , bunu yapmaktan kaçınmalıyız.

"Bir kategoride bildirilen yöntemin adı, orijinal sınıftaki bir yöntemle veya aynı sınıftaki başka bir kategorideki (veya hatta bir üst sınıf) bir yöntemle aynıysa, davranış, hangi yöntem uygulamasının kullanıldığına göre tanımlanmamışsa Kendi sınıflarınızla kategoriler kullanıyorsanız bu sorun olma olasılığı daha düşüktür, ancak standart Cocoa veya Cocoa Touch sınıflarına yöntem eklemek için kategorileri kullanırken sorunlara neden olabilir. "


2

Swift'i kullanma:

override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)
    if self.navigationController?.topViewController != self {
        print("back button tapped")
    }
}

İOS 10'dan itibaren ve belki de daha önce, bu artık çalışmıyor.
Murray Sagal

2

İşte @oneway'in gezinme çubuğu geri düğmesi olayını tetiklenmeden önce yakalamak için yanıtının Swift 3 sürümü . As UINavigationBarDelegateiçin kullanılamaz UIViewController, ne zaman tetiklenecek bir temsilci oluşturmak için gereken navigationBar shouldPopdenir.

@objc public protocol BackButtonDelegate {
      @objc optional func navigationShouldPopOnBackButton() -> Bool 
}

extension UINavigationController: UINavigationBarDelegate  {

    public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {

        if viewControllers.count < (navigationBar.items?.count)! {                
            return true
        }

        var shouldPop = true
        let vc = self.topViewController

        if vc.responds(to: #selector(vc.navigationShouldPopOnBackButton)) {
            shouldPop = vc.navigationShouldPopOnBackButton()
        }

        if shouldPop {
            DispatchQueue.main.async {
                self.popViewController(animated: true)
            }
        } else {
            for subView in navigationBar.subviews {
                if(0 < subView.alpha && subView.alpha < 1) {
                    UIView.animate(withDuration: 0.25, animations: {
                        subView.alpha = 1
                    })
                }
            }
        }

        return false
    }
}

Ardından, görünüm denetleyicinize temsilci işlevini ekleyin:

class BaseVC: UIViewController, BackButtonDelegate {
    func navigationShouldPopOnBackButton() -> Bool {
        if ... {
            return true
        } else {
            return false
        }        
    }
}

Kullanıcıların geri dönmek isteyip istemediklerine karar vermek için sık sık bir uyarı denetleyicisi eklemek istediğimizi fark ettim. Her zaman can Eğer öyleyse return falseiçinde navigationShouldPopOnBackButton()işlev ve böyle bir şey yaparak görünüm denetleyicisi kapatmak:

func navigationShouldPopOnBackButton() -> Bool {
     let alert = UIAlertController(title: "Warning",
                                          message: "Do you want to quit?",
                                          preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "Yes", style: .default, handler: { UIAlertAction in self.yes()}))
            alert.addAction(UIAlertAction(title: "No", style: .cancel, handler: { UIAlertAction in self.no()}))
            present(alert, animated: true, completion: nil)
      return false
}

func yes() {
     print("yes")
     DispatchQueue.main.async {
            _ = self.navigationController?.popViewController(animated: true)
        }
}

func no() {
    print("no")       
}

Bir hata alıyorum: Value of type 'UIViewController' has no member 'navigationShouldPopOnBackButton' kodunu derlemeye çalıştığımda, satır için if vc.responds(to: #selector(v...Ayrıca, self.topViewControlleristeğe bağlı döndürür ve bunun için bir uyarı var.
Sankar

FWIW, bu kodu yaparak sabitledim: let vc = self.topViewController as! MyViewControllerve şimdiye kadar iyi çalışıyor gibi görünüyor. Bunun doğru bir değişiklik olduğunu düşünüyorsanız kodu düzenleyebilirsiniz. Ayrıca, bunun yapılmaması gerektiğini düşünüyorsanız, nedenini bilmekten memnuniyet duyarız. Bu kod için teşekkürler. Muhtemelen bu konuda bir blog yazısı yazmalısınız, çünkü bu cevap oylara göre gömülüdür.
Sankar

@SankarP Bu hatayı almanızın nedeni sizin MyViewControlleruygun olmayabilir BackButtonDelegate. Açma işlemini zorlamak yerine, guard let vc = self.topViewController as? MyViewController else { return true }olası çökmeleri önlemek için yapmanız gerekir .
Lawliet

Teşekkürler. Bence bekçi ifadesi şu şekilde olmalı: guard let vc = self.topViewController as? MyViewController else { self.popViewController(animated: true) return true }ekranın doğru bir şekilde yayınlanamaması durumunda doğru sayfaya hareket ettiğinden emin olmak için. Şimdi navigationBarişlev sadece bu kodun bulunduğu viewcontroller değil tüm VC'lerde çağrıldığını anlıyorum . Cevabınızdaki kodu da güncellemeniz iyi olabilir mi? Teşekkürler.
Sankar

2

Swift 4 iOS 11.3 Sürümü:

Bu, https://stackoverflow.com/a/34343418/4316579 adresinden kgaidis'in cevabına dayanıyor.

Uzantının ne zaman çalışmayı bıraktığından emin değilim, ancak bu yazma sırasında (Swift 4), aşağıda açıklandığı gibi UINavigationBarDelegate uygunluğunu bildirmediğiniz sürece uzantı artık yürütülmeyecek gibi görünüyor.

Umarım bu, uzantılarının neden çalışmadığını merak eden insanlara yardımcı olur.

extension UINavigationController: UINavigationBarDelegate {
    public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {

    }
}

1

Şu anda 'nil' bıraktığınız hedef ve eylem değişkenlerini kullanarak, kaydet iletişim kutularınızı düğme "seçili" olduğunda çağrılacak şekilde bağlayabilmelisiniz. Dikkat edin, bu garip anlarda tetiklenebilir.

Çoğunlukla Amagrammer ile hemfikirim, ancak düğmeyi okla özel hale getirmenin zor olacağını düşünmüyorum. Sadece geri düğmesini yeniden adlandırır, ekran görüntüsü alır, gerekli düğme boyutunu seçer ve düğmenizin üstündeki görüntü olur.


Photoshop yapabileceğinize katılıyorum ve gerçekten isteseydim bunu yapabileceğimi düşünüyorum ama şimdi görünüşü değiştirmeye karar verdim ve bunu istediğim gibi çalışmak için biraz hissetmeye karar verdim.
John Ballinger

Evet, ancak backBarButtonItem öğesine eklendiklerinde eylemler tetiklenmez. Bunun bir hata veya özellik olup olmadığını bilmiyorum; Apple'ın bile bilmemesi mümkündür. Photoshopping alıştırmasına gelince, Apple'ın kanonik bir sembolü kötüye kullanmaktan dolayı uygulamayı reddeteceğinden emin olurum.
Amagrammer

Uyarı: bu cevap bir kopyadan birleştirildi.
Şut9

1

NavigationBars Sağ Düğmesi öğesine erişmeyi ve selector özelliğini ayarlamayı deneyebilirsiniz ... her bir başvuru UIBarButtonItem başvurusu , def çalışacak bu doenst çalışması ise, gezinme çubuğunun sağ düğme öğesini özel bir UIBarButtonItem olarak ayarlamanız seçicisini oluştur ve ayarla ... umarım bu yardımcı olur


Uyarı: bu cevap bir kopyadan birleştirildi.
Şut9

1

Bunun gibi kullanıcı girişi gerektiren bir form için, bunu gezinti yığınınızın bir parçası yerine "kalıcı" olarak çağırmanızı öneririm. Bu şekilde formdaki işlerle ilgilenmek zorundalar, o zaman doğrulayabilir ve özel bir düğme kullanarak işten çıkarabilirsiniz. Uygulamanızın geri kalanıyla aynı görünen ancak size daha fazla kontrol sağlayan bir gezinme çubuğu bile tasarlayabilirsiniz.


Uyarı: bu cevap bir kopyadan birleştirildi.
Şut9

1

Geri düğmesini durdurmak için şeffaf bir UIControl ile örtün ve dokunuşları engelleyin.

@interface MyViewController : UIViewController
{
    UIControl   *backCover;
    BOOL        inhibitBackButtonBOOL;
}
@end

@implementation MyViewController
-(void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    // Cover the back button (cannot do this in viewWillAppear -- too soon)
    if ( backCover == nil ) {
        backCover = [[UIControl alloc] initWithFrame:CGRectMake( 0, 0, 80, 44)];
#if TARGET_IPHONE_SIMULATOR
        // show the cover for testing
        backCover.backgroundColor = [UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:0.15];
#endif
        [backCover addTarget:self action:@selector(backCoverAction) forControlEvents:UIControlEventTouchDown];
        UINavigationBar *navBar = self.navigationController.navigationBar;
        [navBar addSubview:backCover];
    }
}

-(void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];

    [backCover removeFromSuperview];
    backCover = nil;
}

- (void)backCoverAction
{
    if ( inhibitBackButtonBOOL ) {
        NSLog(@"Back button aborted");
        // notify the user why...
    } else {
        [self.navigationController popViewControllerAnimated:YES]; // "Back"
    }
}
@end

Uyarı: bu cevap bir kopyadan birleştirildi.
Şok9

1

En azından Xcode 5'te basit ve oldukça iyi (mükemmel değil) bir çözüm var. IB'de, bir Çubuk Düğme Öğesini Yardımcı Programlar bölmesinden sürükleyin ve Gezinme Çubuğu'nun sol tarafındaki Geri düğmesinin bulunduğu yere bırakın. Etiketi "Geri" olarak ayarlayın. IBAction'ınıza bağlayabileceğiniz ve viewController'ınızı kapatabileceğiniz işlevsel bir düğmeye sahip olacaksınız. Biraz iş yapıyorum ve sonra bir gevşeme segueini tetikliyorum ve mükemmel çalışıyor.

İdeal olmayan, bu düğmenin <oku almaması ve önceki VC'lerin başlığını taşımamasıdır, ancak bence bu yönetilebilir. Benim amacım için, yeni Geri düğmesini bir "Bitti" düğmesi olarak ayarladım, böylece amacı açık.

Ayrıca IB gezgininde iki Geri düğmesi bulunur, ancak netlik için etiketlemek yeterince kolaydır.

resim açıklamasını buraya girin


1

hızlı

override func viewWillDisappear(animated: Bool) {
    let viewControllers = self.navigationController?.viewControllers!
    if indexOfArray(viewControllers!, searchObject: self) == nil {
        // do something
    }
    super.viewWillDisappear(animated)
}

func indexOfArray(array:[AnyObject], searchObject: AnyObject)-> Int? {
    for (index, value) in enumerate(array) {
        if value as UIViewController == searchObject as UIViewController {
            return index
        }
    }
    return nil
}

1

Bu yaklaşım benim için çalıştı (ancak "Geri" düğmesi "<" işaretine sahip olmayacak):

- (void)viewDidLoad
{
    [super viewDidLoad];

    UIBarButtonItem* backNavButton = [[UIBarButtonItem alloc] initWithTitle:@"Back"
                                                                      style:UIBarButtonItemStyleBordered
                                                                     target:self
                                                                     action:@selector(backButtonClicked)];
    self.navigationItem.leftBarButtonItem = backNavButton;
}

-(void)backButtonClicked
{
    // Do something...
    AppDelegate* delegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
    [delegate.navController popViewControllerAnimated:YES];
}

1

@ Onegray cevabının hızlı versiyonu

protocol RequestsNavigationPopVerification {
    var confirmationTitle: String { get }
    var confirmationMessage: String { get }
}

extension RequestsNavigationPopVerification where Self: UIViewController {
    var confirmationTitle: String {
        return "Go back?"
    }

    var confirmationMessage: String {
        return "Are you sure?"
    }
}

final class NavigationController: UINavigationController {

    func navigationBar(navigationBar: UINavigationBar, shouldPopItem item: UINavigationItem) -> Bool {

        guard let requestsPopConfirm = topViewController as? RequestsNavigationPopVerification else {
            popViewControllerAnimated(true)
            return true
        }

        let alertController = UIAlertController(title: requestsPopConfirm.confirmationTitle, message: requestsPopConfirm.confirmationMessage, preferredStyle: .Alert)

        alertController.addAction(UIAlertAction(title: "Cancel", style: .Cancel) { _ in
            dispatch_async(dispatch_get_main_queue(), {
                let dimmed = navigationBar.subviews.flatMap { $0.alpha < 1 ? $0 : nil }
                UIView.animateWithDuration(0.25) {
                    dimmed.forEach { $0.alpha = 1 }
                }
            })
            return
        })

        alertController.addAction(UIAlertAction(title: "Go back", style: .Default) { _ in
            dispatch_async(dispatch_get_main_queue(), {
                self.popViewControllerAnimated(true)
            })
        })

        presentViewController(alertController, animated: true, completion: nil)

        return false
    }
}

Şimdi herhangi bir denetleyicide, sadece uygun RequestsNavigationPopVerificationve bu davranış varsayılan olarak benimsenmiştir.


1

kullanım isMovingFromParentViewController

override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(true)

    if self.isMovingFromParentViewController {
        // current viewController is removed from parent
        // do some work
    }
}

Bunun, geri düğmesine nasıl dokunulduğunu nasıl anlatabilir misiniz?
Murray Sagal

Bu basit, ancak yalnızca yükleyebileceğiniz herhangi bir alt görünümden bu görünüme geri döneceğinizden eminseniz işe yarar. Çocuk üst öğeye geri dönerken bu görünümü atlarsa, kodunuz çağrılmaz (görünüm üst öğeden taşınmadan zaten kaybolmuştu). Ancak bu, yalnızca OP düğmesinin tetiklediği olayların işlenmesi ile aynı sorun. Bu soruya basit bir cevap.
CMont

Bu süper basit ve zarif. Onu seviyorum. Tek bir sorun: kullanıcı aynı zamanda ortada iptal etseler bile geri dönmek için hızlıca kaydırır. Belki de daha iyi bir çözüm bu kodu koymak olacaktır viewDidDisappear. Bu şekilde görüş kesinlikle gittiğinde ateşlenir.
Phontaine Judd

1

@William'ın yanıtı doğrudur, ancak kullanıcı hızlıca kaydırmak için bir hareket başlatmaya başlarsa, viewWillDisappearyöntem çağrılır ve hatta hızlıca kaydırsa bile selfgezinme yığınında self.navigationController.viewControllersolmayacaktır (yani, içermez self) tamamlanmadı ve görünüm denetleyicisi gerçekten atmadı. Böylece çözüm:

  1. Geri gitmek için hızlıca kaydırma hareketini devre dışı bırakın ve aşağıdakileri viewDidAppearkullanarak yalnızca geri düğmesini kullanmaya izin verin:

    if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)])
    {
        self.navigationController.interactivePopGestureRecognizer.enabled = NO;
    }
  2. Veya viewDidDisappearbunun yerine aşağıdaki gibi kullanın :

    - (void)viewDidDisappear:(BOOL)animated
    {
        [super viewDidDisappear:animated];
        if (![self.navigationController.viewControllers containsObject:self])
        {
            // back button was pressed or the the swipe-to-go-back gesture was
            // completed. We know this is true because self is no longer
            // in the navigation stack.
        }
    }

0

Şimdiye kadar bulduğum çözüm çok hoş değil, ama benim için çalışıyor. Bu yanıtı alarak , ben de programlı olarak haşhaş olup olmadığını kontrol:

- (void)viewWillDisappear:(BOOL)animated {
  [super viewWillDisappear:animated];

  if ((self.isMovingFromParentViewController || self.isBeingDismissed)
      && !self.isPoppingProgrammatically) {
    // Do your stuff here
  }
}

Programlı olarak açmadan önce bu özelliği denetleyicinize eklemeniz ve EVET olarak ayarlamanız gerekir:

self.isPoppingProgrammatically = YES;
[self.navigationController popViewControllerAnimated:YES];

0

Yeni bir yol buldum:

Objective-C

- (void)didMoveToParentViewController:(UIViewController *)parent{
    if (parent == NULL) {
        NSLog(@"Back Pressed");
    }
}

hızlı

override func didMoveToParentViewController(parent: UIViewController?) {
    if parent == nil {
        println("Back Pressed")
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.