ios 在导航控制器中设置后退按钮的动作
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1214965/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Setting action for back button in navigation controller
提问by Parrots
I'm trying to overwrite the default action of the back button in a navigation controller. I've provided a target an action on the custom button. The odd thing is when assigning it though the backbutton attribute it doesn't pay attention to them and it just pops the current view and goes back to the root:
我正在尝试覆盖导航控制器中后退按钮的默认操作。我在自定义按钮上提供了一个目标操作。奇怪的是在分配它时虽然 backbutton 属性它不注意它们,它只是弹出当前视图并返回到根:
UIBarButtonItem *backButton = [[UIBarButtonItem alloc]
initWithTitle: @"Servers"
style:UIBarButtonItemStylePlain
target:self
action:@selector(home)];
self.navigationItem.backBarButtonItem = backButton;
As soon as I set it through the leftBarButtonItem
on the navigationItem
it calls my action, however then the button looks like a plain round one instead of the arrowed back one:
一旦我通过它设置它leftBarButtonItem
,navigationItem
它就会调用我的操作,但是按钮看起来像一个普通的圆形而不是带箭头的背面:
self.navigationItem.leftBarButtonItem = backButton;
How can I get it to call my custom action before going back to the root view? Is there a way to overwrite the default back action, or is there a method that is always called when leaving a view (viewDidUnload
doesn't do that)?
我怎样才能让它在返回根视图之前调用我的自定义操作?有没有办法覆盖默认的后退动作,或者是否有一种在离开视图时始终调用的方法(viewDidUnload
不这样做)?
回答by William Jockusch
Try putting this into the view controller where you want to detect the press:
尝试将其放入要检测新闻的视图控制器中:
-(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];
}
回答by onegray
I've implemented UIViewController-BackButtonHandlerextension. It does not need to subclass anything, just put it into your project and override navigationShouldPopOnBackButton
method in UIViewController
class:
我已经实现了UIViewController-BackButtonHandler扩展。它不需要子类化任何东西,只需将其放入您的项目并覆盖类中的navigationShouldPopOnBackButton
方法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
}
回答by HansPinckaers
回答by kgaidis
Swift Version:
迅捷版:
(of https://stackoverflow.com/a/19132881/826435)
(https://stackoverflow.com/a/19132881/826435)
In your view controller you just conform to a protocol and perform whatever action you need:
在您的视图控制器中,您只需遵守协议并执行您需要的任何操作:
extension MyViewController: NavigationControllerBackButtonDelegate {
func shouldPopOnBackButtonPress() -> Bool {
performSomeActionOnThePressOfABackButton()
return false
}
}
Then create a class, say NavigationController+BackButton
, and just copy-paste the code below:
然后创建一个类,比如说NavigationController+BackButton
,复制粘贴下面的代码:
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
}
}
回答by psycho
For some threading reasons, the solution mentionned by @HansPinckaers wasn't right for me, but I found a very easier way to catch a touch on the back button, and I wanna pin this down here in case this could avoid hours of deceptions for someone else. The trick is really easy : just add a transparent UIButton as a subview to your UINavigationBar, and set your selectors for him as if it was the real button! Here's an example using Monotouch and C#, but the translation to objective-c shouldn't be too hard to find.
由于某些线程原因,@HansPinckaers 提到的解决方案不适合我,但我找到了一种非常简单的方法来触摸后退按钮,我想把它固定在这里,以防万一这可以避免数小时的欺骗其他人。诀窍很简单:只需添加一个透明的 UIButton 作为 UINavigationBar 的子视图,并为他设置选择器,就好像它是真正的按钮一样!下面是一个使用 Monotouch 和 C# 的示例,但到 Objective-c 的翻译应该不难找到。
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
}
}
Fun fact : for testing purposes and to find good dimensions for my fake button, I set its background color to blue... And it shows behindthe back button! Anyway, it still catches any touch targetting the original button.
有趣的事实:为了测试目的并为我的假按钮找到合适的尺寸,我将它的背景颜色设置为蓝色......它显示在后退按钮后面!无论如何,它仍然可以捕获针对原始按钮的任何触摸。
回答by Alex Reynolds
It isn't possible to do directly. There are a couple alternatives:
直接做是不可能的。有几种选择:
- Create your own custom
UIBarButtonItem
that validates on tap and pops if the test passes - Validate the form field contents using a
UITextField
delegate method, such as-textFieldShouldReturn:
, which is called after theReturn
orDone
button is pressed on the keyboard
- 创建您自己的自定义
UIBarButtonItem
,如果测试通过,则在点击和弹出时进行验证 - 使用
UITextField
委托方法验证表单字段内容,例如在键盘上按下或按钮-textFieldShouldReturn:
后调用的方法Return
Done
The downside of the first option is that the left-pointing-arrow style of the back button cannot be accessed from a custom bar button. So you have to use an image or go with a regular style button.
第一个选项的缺点是无法从自定义栏按钮访问后退按钮的向左箭头样式。因此,您必须使用图像或使用常规样式按钮。
The second option is nice because you get the text field back in the delegate method, so you can target your validation logic to the specific text field sent to the delegate call-back method.
第二个选项很好,因为您可以在委托方法中返回文本字段,因此您可以将验证逻辑定位到发送到委托回调方法的特定文本字段。
回答by Jason Moore
This technique allows you to change the text of the "back" button without affecting the title of any of the view controllers or seeing the back button text change during the animation.
这种技术允许您更改“后退”按钮的文本,而不会影响任何视图控制器的标题或在动画过程中看到后退按钮文本的变化。
Add this to the init method in the callingview controller:
将此添加到调用视图控制器的 init 方法中:
UIBarButtonItem *temporaryBarButtonItem = [[UIBarButtonItem alloc] init];
temporaryBarButtonItem.title = @"Back";
self.navigationItem.backBarButtonItem = temporaryBarButtonItem;
[temporaryBarButtonItem release];
回答by Jawad Zeb
Easiest way
最简单的方法
You can use the UINavigationController's delegate methods. The method willShowViewController
is called when the back button of your VC is pressed.do whatever you want when back btn pressed
您可以使用 UINavigationController 的委托方法。willShowViewController
当按下 VC 的后退按钮时调用该方法。按下后退按钮时做任何你想做的事
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated;
回答by Sarasranglt
Found a solution which retains the back button style as well. Add the following method to your view controller.
找到了一个保留后退按钮样式的解决方案。将以下方法添加到您的视图控制器。
-(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];
}
Now provide a functionality as needed in the following method:
现在根据需要在以下方法中提供功能:
-(void)backAction:(UIBarButtonItem *)sender {
//Your functionality
}
All it does is to cover the back button with a transparent button ;)
它所做的只是用透明按钮覆盖后退按钮;)
回答by brahimm
Overriding navigationBar(_ navigationBar:shouldPop): This is nota good idea, even if it works. for me it generated random crashes on navigating back. I advise you to just override the back button by removing the default backButton from navigationItem and creating a custom back button like below:
Overriding navigationBar(_ navigationBar:shouldPop):这不是一个好主意,即使它有效。对我来说,它在导航回来时产生了随机崩溃。我建议您通过从导航项中删除默认的 backButton 并创建一个自定义后退按钮来覆盖后退按钮,如下所示:
override func viewDidLoad(){
super.viewDidLoad()
navigationItem.leftBarButton = .init(title: "Go Back", ... , action: #selector(myCutsomBackAction)
...
}
========================================
========================================
Building on previous responses with UIAlertin Swift5in a Asynchronousway
以异步方式在Swift5中使用UIAlert 的先前响应构建
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
}
}
On your controller
在您的控制器上
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)
})
])
}
}