iOS 7自定义后退按钮

时间:2013-09-16 09:42:49

标签: ios cocoa-touch ios7 xcode5

我想使用自定义后退按钮。在iOS 6中,一切都很完美,但iOS 7很奇怪。

[[UIBarButtonItem appearance] setBackButtonBackgroundImage:[[UIImage imageNamed:@"back_button_normal"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 12.0, 0, 12.0)] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];

首先,它没有iOS 7箭头,也没有背景图像。

(俄语语言环境)

initial state

然后,如果按下按钮,则会出现背景图像。此外,我为UIControlStateHighlighted状态设置了背景图像,当按住按钮时,按下突出显示的图像也会出现。按下任何后退按钮后,所有后退按钮都有背景图像。

once pressed

BUT!如果您提供模态视图控制器,将其关闭,然后按任何视图控制器 - 每个后退按钮都会出现iOS 7箭头。

我使用DP5。那是UIKit的错误吗?

PS我还尝试手动创建后退按钮,使用UIBarButtonItem,将背景图像设置为它,然后self.navigationItem.backBarButtonItem = barButtonItem;没有帮助。 然后我尝试将背景图像设置为禁用状态并更改我的栏按钮项的启用属性,也没有帮助。

enter image description here

12 个答案:

答案 0 :(得分:50)

这不是错误,这是{7}在iOS 7中的显示方式。例如:

enter image description here

您可能应该为您的应用程序使用新概念,而不是在iOS 7中为后退按钮设置背景图像。

如果您仍然希望您的后退按钮与iOS6中的相同,那么您应该手动创建这些后退按钮:

Back button

修改:不打破滑动手势Here is a source)

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

答案 1 :(得分:22)

第一次推送时未出现的自定义背景图片已在iOS 7 GM中修复。

要隐藏标准后退指示符,请使用以下代码:

if ([UINavigationBar instancesRespondToSelector:@selector(setBackIndicatorImage:)]) { // iOS 7
    [navigationBarAppearance setBackIndicatorImage:[UIImage imageNamed:@"transparent_1px"]];
    [navigationBarAppearance setBackIndicatorTransitionMaskImage:[UIImage imageNamed:@"transparent_1px"]];
}

答案 2 :(得分:13)

据我所知,最初没有出现的自定义背景图像在iOS7 GM或final中没有修复。我看到了同样的问题。它似乎确实是一个苹果虫; Apple使用的私有视图在初始显示时只需要setNeedsDisplay调用。对它做任何导致该调用的事情应该修复它 - 比如按下它(这可能会改变内部状态,因此它自己调用setNeedsDisplay),或者带上一个模态(这可能会强制重新显示下一个整个视图层次结构) viewWillAppear:call)。

使用leftBarItems也可以工作,但这可能会导致现有代码的许多维护问题(某些屏幕可能有自己的左侧项目,例如,当设置回nil时,它们会恢复原始的后退项目)。

如上所述,理想情况下,您可以在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

注意:UINavigationBar当前覆盖了相关方法,因此我希望使用method_exchangeImplementations样式。我刚刚添加其他东西以确保安全,以防Apple改变他们的代码。我们可能自己没有边界,但我确实发现这种方法可以作为一种选择(直到更彻底的UI提升),至少。

附加说明:此错误似乎已在iOS 7.1中修复。因此,如果运行&gt; = 7.0和&lt;&gt;,则可以将补丁条件化为仅安装方法。 7.1。

答案 3 :(得分:6)

有一个更好的解决方案,不涉及方法调配。

您需要在应用中的某处添加UINavigationViewControllerDelegate方法。

- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
dispatch_async(dispatch_get_main_queue(), ^{
    [[navigationController.navigationBar subviews] makeObjectsPerformSelector:@selector(setNeedsDisplay)];
});

}

答案 4 :(得分:3)

我的解决方案适用于iOS 7及更高版本。

首先,使默认后退按钮不可见。

self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];

然后,使用自定义图像设置后退按钮的默认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];

此时,请自定义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

然后,将其设置为我的navigationBar

// set custom NavagationBar for back button position
[self.navigationController setValue:[[SANavigationBar alloc] init] forKey:@"navigationBar"];

答案 5 :(得分:1)

在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;

答案 6 :(得分:1)

使用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
    }
}

在你的UIViewController中使用如下:

self.enableCustomBackButtom()

答案 7 :(得分:0)

我刚刚提供了与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];
        }

答案 8 :(得分:0)

我的解决方案是在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];
}

子类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;
}

答案 9 :(得分:0)

这对我有用:

- (void)setCustomNavigationBackButton
{    
  self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];

  UIImage *myIcon = [self imageWithImage:[UIImage imageNamed:@"backbutton.png"] scaledToSize:CGSizeMake(20, 20)];

  self.navigationController.navigationBar.backIndicatorImage = myIcon;
  self.navigationController.navigationBar.backIndicatorTransitionMaskImage = myIcon;
}

- (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize 
{
  //UIGraphicsBeginImageContext(newSize);
  // In next line, pass 0.0 to use the current device's pixel scaling factor (and thus account for Retina resolution).
  // Pass 1.0 to force exact pixel size.
  UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);
  [image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
  UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();
  return newImage;
}

此外,自定义颜色的自定义字体:

//self.navigationController.navigationBar.tintColor = [UIColor whiteColor];

[[UIBarButtonItem appearanceWhenContainedIn:[UINavigationBar class], nil] setTitleTextAttributes:
 @{NSForegroundColorAttributeName:[UIColor whiteColor],
   NSFontAttributeName:[UIFont fontWithName:@"Signika-Bold" size:20]}

forState:UIControlStateNormal];

参考:https://stackoverflow.com/a/2658801/1371949

答案 10 :(得分:0)

我使用下面的代码,这些代码适用于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];

答案 11 :(得分:-1)

-(void) viewWillAppear:(BOOL)animated
{
    UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
    [btn setFrame:CGRectMake(0, 0, 30, 44)];
    [btn setImage:[UIImage imageNamed:@"btnBack.png"] forState:UIControlStateNormal];
    [btn addTarget:self action:@selector(PopToView) forControlEvents:UIControlEventTouchUpInside];
    UIBarButtonItem *btnBack = [[UIBarButtonItem alloc] initWithCustomView:btn];
    [btnBack setTintColor:[UIColor whiteColor]];
    [[self.navigationController navigationItem] setLeftBarButtonItem:btnBack];

}