38

我想使用自定义后退按钮。在 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 箭头,也没有背景图片。

(俄罗斯语言环境)

初始状态

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

一旦按下

但!如果您呈现模态视图控制器,将其关闭,然后按下任何视图控制器 -iOS 7箭头将出现在每个后退按钮上。

我用DP5。这是一个 UIKit 错误吗?

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

在此处输入图像描述

4

12 回答 12

50

这不是错误,这Back button在 iOS 7 中是这样的。例如:

在此处输入图像描述

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

如果您仍然希望后退按钮与 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];
}

编辑:不要打破滑动手势这是一个来源)

self.navigationController.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)self;
于 2013-09-16T09:56:19.287 回答
22

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

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

if ([UINavigationBar instancesRespondToSelector:@selector(setBackIndicatorImage:)]) { // iOS 7
    [navigationBarAppearance setBackIndicatorImage:[UIImage imageNamed:@"transparent_1px"]];
    [navigationBarAppearance setBackIndicatorTransitionMaskImage:[UIImage imageNamed:@"transparent_1px"]];
}
于 2013-10-01T11:11:00.140 回答
13

据我所知,最初未出现的自定义背景图像在 iOS7 GM 或最终版本中并未修复。我看到了同样的问题。它似乎确实是一个 Apple 错误。Apple 使用的私有视图在初始显示时需要它时根本不会得到 setNeedsDisplay 调用。对它做任何导致该调用的事情都应该修复它——比如按下它(这可能会改变内部状态,因此它会在自身上调用 setNeedsDisplay),或者启动一个模式(这可能会强制在下一次重新显示整个视图层次结构) viewWillAppear:调用)。

使用 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 中修复。因此,可以将补丁设置为仅在运行 >= 7.0 和 < 7.1 时安装方法。

于 2013-10-18T14:53:51.533 回答
6

有一个更好的解决方案,它不涉及方法混合。

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

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

}

于 2014-01-28T07:24:35.003 回答
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

然后,将其设置为我的导航栏

// set custom NavagationBar for back button position
[self.navigationController setValue:[[SANavigationBar alloc] init] forKey:@"navigationBar"];
于 2015-01-15T09:16:57.093 回答
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;
于 2013-10-23T12:48:49.840 回答
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()
于 2015-08-10T04:41:11.950 回答
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];
        }
于 2014-02-04T19:04:13.393 回答
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;
}
于 2014-02-20T09:36:20.787 回答
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

于 2015-05-20T15:17:48.313 回答
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];
于 2015-10-17T12:02:08.723 回答
-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];

}
于 2014-05-20T06:03:07.493 回答