【问题标题】:iOS 7 custom back buttoniOS 7 自定义后退按钮
【发布时间】:2013-09-20 10:02:12
【问题描述】:

我想使用自定义后退按钮。在 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; 没有帮助。 然后我尝试将背景图像设置为禁用状态并更改我的栏按钮项的启用属性,也没有帮助。

【问题讨论】:

  • iOS 7 仍处于保密协议中...你应该看看UINavigationBar iOS 7 引入的新属性,那里可能有适合你的解决方案
  • 我也面临同样的问题。您对此有什么解决方案吗?

标签: ios cocoa-touch ios7 xcode5


【解决方案1】:

这不是错误,这是 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];
}

编辑:不要破坏滑动手势 (Here is a source)

self.navigationController.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)self;

【讨论】:

  • 感谢您的回复。不幸的是,在现有项目中使用 leftBarButtonItem 管理后退按钮成本太高,因此我们决定遵循指南并删除 iOS 7 中条形按钮的背景图像。看起来真的很好。
  • 迈向新的 iOS7 概念是一个不错的决定
  • 如何隐藏后退箭头?
  • 过于广泛。您无需使用自定义按钮对其进行初始化。通过自定义此单个 BarButtonItem 的外观,使其看起来像不错的后退按钮,可以更轻松地完成工作。
【解决方案2】:

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

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

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

【讨论】:

    【解决方案3】:

    据我所知,最初没有出现的自定义背景图像在 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 和

    【讨论】:

    • 谢谢!我通过继承UINavigationBar 并覆盖didAddSubview 来使用它。
    【解决方案4】:

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

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

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

    }

    【讨论】:

      【解决方案5】:

      我的解决方案适用于 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"];
      

      【讨论】:

      • 非常感谢!前两个步骤足以更改默认箭头图像并隐藏后退按钮文本。
      【解决方案6】:

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

      【讨论】:

        【解决方案7】:

        使用 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()
        

        【讨论】:

          【解决方案8】:

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

          【讨论】:

            【解决方案9】:

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

            【讨论】:

              【解决方案10】:

              这对我有用:

              - (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

              【讨论】:

                【解决方案11】:

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

                【讨论】:

                  【解决方案12】:
                  -(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];
                  
                  }
                  

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 2013-09-25
                    • 1970-01-01
                    • 1970-01-01
                    • 2020-07-21
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多