iOS 7 自定义后退按钮
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18824887/
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
iOS 7 custom back button
提问by storoj
I want to use custom back button. in iOS 6 everything is perfect but iOS 7
is strange.
我想使用自定义后退按钮。在 iOS 6 中,一切都很完美,但iOS 7
很奇怪。
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:[[UIImage imageNamed:@"back_button_normal"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 12.0, 0, 12.0)] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
first, it has no iOS 7 arrow and no background image.
首先,它没有 iOS 7 箭头和背景图像。
(Russian locale)
(俄罗斯语言环境)
then, if you press the button background image appears. Also i had background image set for UIControlStateHighlighted
state and when you hold the button pressed highlighted image appears too. After any back button once pressed all back buttons have background image.
然后,如果你按下按钮背景图像出现。我还为UIControlStateHighlighted
状态设置了背景图像,当您按住按钮时,也会出现突出显示的图像。按下任何后退按钮后,所有后退按钮都有背景图像。
BUT! If you present modal view controller, dismiss it, then push any view controller - iOS 7
arrow will appear at every back button.
但!如果您提供模态视图控制器,请关闭它,然后按下任何视图控制器 -iOS 7
每个后退按钮都会出现箭头。
I use DP5. Is that a UIKit bug?
我用DP5。这是一个 UIKit 错误吗?
PS Also i tried to create back button manually, using UIBarButtonItem
, set background image to it, then self.navigationItem.backBarButtonItem = barButtonItem;
Did not help.
Then i tried to set background image to disabled state and change enabled property of my bar button item, did not help too.
PS我也尝试手动创建后退按钮,使用UIBarButtonItem
,设置背景图像,然后self.navigationItem.backBarButtonItem = barButtonItem;
没有帮助。然后我尝试将背景图像设置为禁用状态并更改我的栏按钮项的启用属性,也没有帮助。
回答by B.S.
This is not a bug, this how Back button
looks in iOS 7. For example:
这不是错误,这是Back button
iOS 7 中的样子。例如:
You should probably use the new concept for your application, and not to set background image for back button in iOS 7.
您可能应该为您的应用程序使用新概念,而不是在 iOS 7 中为后退按钮设置背景图像。
If you still want you back button have the same as it looked in iOS6 than you should probably create those back buttons manually:
如果您仍然希望后退按钮与在 iOS6 中看起来相同,那么您可能应该手动创建这些后退按钮:
- (void)loadView
{
[super loadView];
UIButton *backButton = [[UIButton alloc] initWithFrame: CGRectMake(0, 0, 60.0f, 30.0f)];
UIImage *backImage = [[UIImage imageNamed:@"back_button_normal.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 12.0f, 0, 12.0f)];
[backButton setBackgroundImage:backImage forState:UIControlStateNormal];
[backButton setTitle:@"Back" forState:UIControlStateNormal];
[backButton addTarget:self action:@selector(popBack) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *backButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
self.navigationItem.leftBarButtonItem = backButtonItem;
}
-(void) popBack {
[self.navigationController popViewControllerAnimated:YES];
}
Edit: Not to break Swipe Gesture(Here is a source)
编辑:不要打破滑动手势(这是一个来源)
self.navigationController.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)self;
回答by kolyuchiy
The custom background image not appearing on the first push was fixed in iOS 7 GM.
iOS 7 GM 中修复了第一次推送时未出现的自定义背景图像。
To hide standard back indicator use this code:
要隐藏标准后退指示器,请使用以下代码:
if ([UINavigationBar instancesRespondToSelector:@selector(setBackIndicatorImage:)]) { // iOS 7
[navigationBarAppearance setBackIndicatorImage:[UIImage imageNamed:@"transparent_1px"]];
[navigationBarAppearance setBackIndicatorTransitionMaskImage:[UIImage imageNamed:@"transparent_1px"]];
}
回答by Carl Lindberg
The custom background image not appearing initially was not fixed in iOS7 GM or final, as far as I can tell. I see the same problem. It does seem to be an Apple bug; the private view Apple uses simply does not get a setNeedsDisplay call when it needs it on initial display. Doing anything to it which causes that call should fix it -- like pressing on it (which likely changes internal state so it calls setNeedsDisplay on itself), or bringing a modal up (which probably forces a redisplay of the entire view hierarchy on the next viewWillAppear: call).
据我所知,最初未出现的自定义背景图像在 iOS7 GM 或最终版本中并未修复。我看到同样的问题。它似乎确实是 Apple 的错误;Apple 使用的私有视图在初始显示时不需要 setNeedsDisplay 调用。对它做任何导致该调用的事情都应该修复它——比如按下它(这可能会改变内部状态,所以它会调用 setNeedsDisplay 本身),或者打开一个模态(这可能会强制重新显示整个视图层次结构) viewWillAppear中:调用)。
Using leftBarItems instead also can work, but that may cause a lot of maintenance issues with existing code (some screens may have their own left items, expecting that when set back to nil they restore the original back item, for example).
使用 leftBarItems 也可以工作,但这可能会导致现有代码的许多维护问题(例如,某些屏幕可能有自己的左项,期望当设置回 nil 时它们会恢复原始的后项)。
As mentioned, ideally you would be able to change to a borderless look on iOS7, which means that the bug isn't really apparent (since there is no background image). For some iOS6/iOS7 transition situations though, that may be difficult (lots of screens, and/or the need to support older iOS versions for a while and too hard to have two looks implemented, and it doesn't look good borderless without other changes). If that's the case, the following patch should work:
如前所述,理想情况下,您可以在 iOS7 上更改为无边框外观,这意味着该错误并不是很明显(因为没有背景图像)。但是,对于某些 iOS6/iOS7 过渡情况,这可能很困难(大量屏幕,和/或需要支持较旧的 iOS 版本一段时间,而且很难实现两种外观,而且如果没有其他变化)。如果是这种情况,以下补丁应该有效:
#import <objc/runtime.h>
@implementation UINavigationBar (BackButtonDisplayFix)
+ (void)load
{
if ([UIDevice currentDevice].systemVersion.intValue >= 7)
{
/*
* We first try to simply add an override version of didAddSubview: to the class. If it
* fails, that means that the class already has its own override implementation of the method
* (which we are expecting in this case), so use a method-swap version instead.
*/
Method didAddMethod = class_getInstanceMethod(self, @selector(_displaybugfixsuper_didAddSubview:));
if (!class_addMethod(self, @selector(didAddSubview:),
method_getImplementation(didAddMethod),
method_getTypeEncoding(didAddMethod)))
{
Method existMethod = class_getInstanceMethod(self, @selector(didAddSubview:));
Method replacement = class_getInstanceMethod(self, @selector(_displaybugfix_didAddSubview:));
method_exchangeImplementations(existMethod, replacement);
}
}
}
- (void)_displaybugfixsuper_didAddSubview:(UIView *)subview
{
[super didAddSubview:subview];
[subview setNeedsDisplay];
}
- (void)_displaybugfix_didAddSubview:(UIView *)subview
{
[self _displaybugfix_didAddSubview:subview]; // calls the existing method
[subview setNeedsDisplay];
}
@end
Note: UINavigationBar does currently have an override of the method in question, so I'd expect the method_exchangeImplementations style to be used. I just added the other stuff for safety in case Apple changes their code. We may go borderless ourselves, but I did find this approach worked as an option (until a more thorough UI uplift), at least.
注意: UINavigationBar 当前确实覆盖了所讨论的方法,因此我希望使用 method_exchangeImplementations 样式。为了安全起见,我只是添加了其他东西,以防 Apple 更改他们的代码。我们自己可能会无边界,但我确实发现这种方法是一种选择(直到更彻底的 UI 提升),至少。
Additional note: This bug appears to be fixed in iOS 7.1. So, the patch could be conditionalized to only install the methods if running >= 7.0 and < 7.1.
附加说明:此错误似乎已在 iOS 7.1 中修复。因此,可以将补丁设置为仅在运行 >= 7.0 和 < 7.1 时才安装这些方法。
回答by Arkadiusz M?ynarczyk
There is a better solution that doesn't involve method swizzling.
有一个更好的解决方案,它不涉及方法调配。
You need to add UINavigationViewControllerDelegate method somewhere in your app.
您需要在应用程序的某处添加 UINavigationViewControllerDelegate 方法。
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
dispatch_async(dispatch_get_main_queue(), ^{
[[navigationController.navigationBar subviews] makeObjectsPerformSelector:@selector(setNeedsDisplay)];
});
}
}
回答by Jong Su Park
My solution is for iOS 7 and above.
我的解决方案适用于 iOS 7 及更高版本。
At first, make default back button invisible.
首先,使默认的后退按钮不可见。
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];
then, set default backIndicatorImage
of back button using custom image.
然后,backIndicatorImage
使用自定义图像设置后退按钮的默认值。
[UINavigationBar appearance].backIndicatorImage = [[UIImage imageNamed:@"topbar_icon_back_n.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
[UINavigationBar appearance].backIndicatorTransitionMaskImage = [[UIImage imageNamed:@"topbar_icon_back_p.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
At this point, make custom UINavigationBar
for resizing _UINavigationBarBackIndicatorView
which contains above backIndicatorImage
.
此时,自定义包含上面的UINavigationBar
调整大小。_UINavigationBarBackIndicatorView
backIndicatorImage
const CGPoint SANavigationBarOffset = {-8, 11.5};
@implementation SANavigationBar
- (void)layoutSubviews
{
[super layoutSubviews];
// set back button position
NSArray *classNamesToReposition = @[@"_UINavigationBarBackIndicatorView"];
for (UIView *view in [self subviews]) {
if ([classNamesToReposition containsObject:NSStringFromClass([view class])]) {
CGRect frame = [view frame];
frame.origin.x = 0;
frame.origin.y = 0;
[view setFrame:frame];
}
}
}
@end
then, set it as my navigationBar
然后,将其设置为我的导航栏
// set custom NavagationBar for back button position
[self.navigationController setValue:[[SANavigationBar alloc] init] forKey:@"navigationBar"];
回答by Heberti Almeida
Using Swift you can just add a extension:
使用 Swift 你只需添加一个扩展:
extension UIViewController: UIGestureRecognizerDelegate {
func popBack() {
self.navigationController?.popViewControllerAnimated(true)
}
func enableCustomBackButtom() {
self.navigationItem.leftBarButtonItem = UIBarButtonItem(image: UIImage(named: "icon-back"), style: UIBarButtonItemStyle.Plain, target: self, action:"popBack")
self.navigationController?.interactivePopGestureRecognizer.delegate = self
}
}
And in your UIViewController use like this:
在你的 UIViewController 中使用如下:
self.enableCustomBackButtom()
回答by Chris Alan
Add button as navigation item in ios7 as below
在ios7中添加按钮作为导航项,如下所示
UIButton *btnAdd = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 60, 30)];
[btnAdd setContentMode:UIViewContentModeScaleAspectFit];
[btnAdd setBackgroundImage:[UIImage imageNamed:@"back.png"] forState:UIControlStateNormal];
[btnAdd addTarget:self action:@selector(backButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *btnAdd = [[UIBarButtonItem alloc] initWithCustomView:imView];
self.navigationItem.rightBarButtonItem = btnAdd;
回答by user2884586
I just did it providing the same behaviour as in iOS6 (notice that navigationBar is the UINavigationBar), make sure that navigationBar has a topItem
我只是提供了与 iOS6 相同的行为(注意,navigationBar 是 UINavigationBar),确保 navigationBar 有一个 topItem
UINavigationItem *topItemNavigation = [navigationBar topItem];
UIBarButtonItem *barButtonTopItemNavigation = [[UIBarButtonItem alloc] initWithTitle:topItemNavigation.title style:UIBarButtonItemStyleBordered target:nil action:nil];
[barButtonTopItemNavigation setBackButtonBackgroundImage:YOUR_IMAGE_BACKGROUND forState:UIControlStateNormal barMetrics:UIBarMetricsDefault ];
[topItemNavigation setBackBarButtonItem: barButtonTopItemNavigation];
}
回答by Vladimir Shutyuk
My solution was to write a category on UINavigationItem. This is for iOS7.
我的解决方案是在 UINavigationItem 上写一个类别。这是针对 iOS7 的。
- (void)mdSetCustomBackButton:(UINavigationController *)navigationController
{
MDBackButton *backButton = [[MDBackButton alloc] initWithFrame:CGRectMake(0.0, 0.0, 44.0, 44.0) navigationController:navigationController];
[backButton addTarget:self action:@selector(popBack:) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
[self setLeftBarButtonItem:barButtonItem];
[navigationController.interactivePopGestureRecognizer setDelegate:(id<UIGestureRecognizerDelegate>)self];
}
- (void)popBack:(MDBackButton *)sender
{
[sender.navigationController popViewControllerAnimated:YES];
}
And subclass UIButton to add a UINavigationController property (to pop and set swipe back delegate).
并子类化 UIButton 以添加 UINavigationController 属性(以弹出和设置向后滑动委托)。
@property (nonatomic, weak) UINavigationController *navigationController;
@implementation MDBackButton
- (id)initWithFrame:(CGRect)frame navigationController:(UINavigationController *)navigationController
{
self = [super initWithFrame:frame];
if(self){
_navigationController = navigationController;
[self setImage:[UIImage imageNamed:@"back_button"] forState:UIControlStateNormal];
}
return self;
}
回答by wuxueqian
I use these codes below, which works in iOS 8
我使用下面的这些代码,它适用于 iOS 8
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.translatesAutoresizingMaskIntoConstraints = NO;
button.exclusiveTouch = YES;
button.titleLabel.font = [UIFont systemFontOfSize:14.0];
[button setTitleColor:kWhiteColor forState:UIControlStateNormal];
[button setTitleColor:[UIColor colorWithRed:1/255.0 green:36/255.0 blue:60/255.0 alpha:1.0] forState:UIControlStateHighlighted];
[button setTitle:@"Back" forState:UIControlStateNormal];
[button setImage:[UIImage imageNamed:@"barbutton_back"] forState:UIControlStateNormal];
[button setImageEdgeInsets:UIEdgeInsetsMake(1.0, 0.0, 0.0, 0.0)];
CGSize fontSize = [button.titleLabel sizeThatFits:CGSizeMake(100.0, 30.0)];
button.frame = CGRectMake(0.0, 0.0, button.imageView.image.size.width+fontSize.width, 30.0);
UIBarButtonItem *barbtn = [[UIBarButtonItem alloc] initWithCustomView:button];
//fix iOS 7 left margin
UIBarButtonItem *negativeSpacer = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
negativeSpacer.width = -10;
self.navigationItem.leftBarButtonItems = [NSArray arrayWithObjects:negativeSpacer,barbtn, nil];