【问题标题】:How to adjust UIToolBar left and right padding如何调整 UIToolBar 左右内边距
【发布时间】:2011-08-26 15:23:32
【问题描述】:

我创建一个带有代码的 UIToolbar 和另一个带有界面构建器的 UIToolbar。但是,发现两个工具栏有不同的左右填充,如下所示:

来自界面生成器:

来自代码:

UIImage *buttonImage = [[UIImage imageNamed:@"button.png"] stretchableImageWithLeftCapWidth:10 topCapHeight:0];
UIButton *btnTest = [UIButton buttonWithType:UIButtonTypeCustom];
[btnTest setBackgroundImage:buttonImage forState:UIControlStateNormal];
[btnTest setTitle:@"Back" forState:UIControlStateNormal];   
[btnTest.titleLabel setFont:[UIFont boldSystemFontOfSize:13]];  
[btnTest setBackgroundImage:[imgToolbarButton stretchableImageWithLeftCapWidth:5 topCapHeight:0]  forState:UIControlStateNormal];
[btnTest addTarget:self action:@selector(clearDateEdit:) forControlEvents:UIControlEventTouchUpInside];
btnTest.frame = CGRectMake(0.0, 0.0, 50, 30);
UIBarButtonItem *btnTestItem = [[UIBarButtonItem alloc] initWithCustomView:btnTest];
[self.toolbar setItems:[NSArray arrayWithObjects:btnTestItem,nil]];
[btnTestItem release];

我的问题是如何通过代码调整UIToolbar的左右填充?

更新

我发现这个对齐问题只发生在 UIBarButtonItem 与 UIButton 的 customView 中,UIBarButtonItem 的对齐很好。知道是什么原因导致或解决此问题。

我现在能想到的唯一解决方案就是手动设置框架。

【问题讨论】:

  • 填充是指按钮之间的间距吗?
  • @TonyMocha 你确定如果你只是通过代码添加两个按钮并在它们之间有一个灵活的空格,它看起来确实不同吗?
  • @peterp。第一个和最后一个按钮的左右填充。
  • @nick weaver。是的。不确定只有我一个人。但不知何故,这两个工具栏似乎都有不同的填充空间。我在挠头。
  • @TonyMocha 看看nib2objc 并让它将您的 nib 文件转换为可读的 objc 代码。可能会更改一些不明显的属性。

标签: iphone objective-c


【解决方案1】:

在我的情况下,我需要减少 leftBarButtonItem 的左侧填充并在整个区域保持触摸。

class FilterButton: Button {

override init() {
    super.init()
    setImage(UIImage(named: "filter"), for: .normal)
    backgroundColor = UIColor.App.ultraLightGray
    layer.cornerRadius = 10
    
    snp.makeConstraints {
        $0.size.equalTo(36)
    }
}

override var alignmentRectInsets: UIEdgeInsets {
    return UIEdgeInsets(top: 0, left: 4, bottom: 0, right: -4)
}
}


let filterButton = FilterButton()
filterButton.addTarget(self, action: #selector(filterTapped(_:)), for: .touchUpInside)
let filterBarButton = UIBarButtonItem(customView: filterButton)
let spacer = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
    
navigationItem.setLeftBarButtonItems([spacer, filterBarButton], animated: false)

【讨论】:

    【解决方案2】:

    我遇到了同样的问题,你可以用 UIBarButtonSystemItemFixedSpace 做一个巧妙的技巧,在你的第一个按钮之前和最后一个按钮之后添加一个负宽度,它会将按钮移动到边缘。

    例如,要去掉右侧边距,请将以下 FixedSpace 栏项添加为最后一项:

    iOS 11 更新至 13

    • 斯威夫特

      let negativeSeperator = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
      negativeSeperator.width = 12
      

      在 Swift 版本中宽度必须为正


    • 对象

      UIBarButtonItem *negativeSeparator = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
      negativeSeparator.width = -12;
      

    左右边距为12px。

    iOS7 更新 - iPhone 上的边距为 16 像素,iPad 上的边距为 20 像素!

    【讨论】:

    • 我不得不使用 -5 的宽度,但这很好用。对于右栏按钮,我必须在它之前和之后添加一个negativeSeparator才能工作。
    • 是的,它可以工作,但它看起来像一个解决方法......不是还有其他解决方案吗?
    • Kevin Elliott 和其他任何感兴趣的人,您不需要在之前和之后使用分隔符,就在之前。我使用的代码是[[self.navigationBar topItem] setRightBarButtonItems:@[negativeSeparator, customButton]];,带有-5 pt 宽的负分隔符。以这种方式添加时,数组中的第一个是最右边的,第二个放在它的左边。将右栏按钮项视为堆栈,这种“反向”排序是有意义的。
    • @craig-mellon 对于 iPhone 6,16px 不起作用。它适用于 20 像素。在所有 iPhone 设备上解决此问题的最佳解决方案是什么。
    • 使用self.navigationController.view.layoutMargins.right 作为实际保证金。 IOS8+
    【解决方案3】:

    只需向UIToolbar前导和尾随约束添加负常量。这样,工具栏在您的视图之外开始和结束,因此具有较少的填充。

    【讨论】:

      【解决方案4】:

      这是一个很好的问题,即使提供了解决方案 - 它也无法解决 iOS11 上的问题。

      工作解决方案

      如果你创建一个UIButton的新扩展:

      class BarButton: UIButton {
      
          override var alignmentRectInsets: UIEdgeInsets {
              return UIEdgeInsetsMake(0, 16, 0, 16)
          }
      
      }
      

      那么当你在你的布局中使用它时:

      lazy var btnLogin: BarButton = {
          let btn = BarButton(type: .custom)
          // Add your button settings
      
          btn.widthAnchor.constraint(equalToConstant: self.frame.size.width).isActive = true
          btn.heightAnchor.constraint(equalToConstant: 50).isActive = true
          btn.translatesAutoresizingMaskIntoConstraints = false
          return btn
      }()
      

      这解决了一个非常烦人的问题,如果您有任何问题,请大声疾呼。

      【讨论】:

      • 这是一种更简洁的方法,应该是公认的解决方案
      • 问题已解决但有副作用,如果自定义按钮的背景与下一个/上一个控制器中的导航栏不同,则在推/弹出时会显示一个剪辑按钮。
      • 这是一个很好的解决方案,但对我来说,触摸区域已关闭。我必须添加上面提到的“positiveSeparator”来移动触摸区域。
      【解决方案5】:

      iOS 11 之前

      您有三种可能的解决方案:

      • 第一个是使用固定宽度为负的空间项 UIBarButtonItem(barButtonSystemItem: .fixedSpace ...)
      • 另一个是在自定义视图上覆盖alignmentRectInsets
      • 最后一个,如果你使用 UIButton 作为你项目的自定义视图,你可以覆盖 contentEdgeInsets 和 hitTest(_:with:)

      当然,第一个解决方案似乎比其他解决方案更好

      UIBarButtonItem *negativeSeparator = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
      negativeSeparator.width = -12;
      

      如果你使用swift,代码会是这样的

      var negativeSeparator = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
      negativeSeparator.width = -12
      

      iOS 11 之后(包含 iOS 11)

      很遗憾,所有的解决方案都不能在 iOS 11 中使用

      • 第一个解决方案:设置负宽度不再有效
      • 第二种解决方案:覆盖alignmentRectInsets会导致部分item不再接收触摸
      • 最后一个解决方案:我认为覆盖 hitTest(_:with:) 不是一个好主意,请不要这样做!

      您也可以在Developer Forums 上看到一些建议,但是当您查看所有评论后,您会发现有关此主题的建议是创建自定义 UINavigationBar 子类并做一些复杂的事情。

      我的天啊,我想做的只是改变填充,而不是导航栏!

      幸运的是,我们可以在 iOS 11 中使用一个技巧来做到这一点!

      我们开始:

      1。创建自定义按钮

      • 将 translatesAutoresizingMaskIntoConstraints 设置为 false
      • 覆盖alignmentRectInsets
        • 右侧的项目使用 UIEdgeInsets(top: 0, left: -8, bottom: 0, right: 8)
        • 左侧项目使用 UIEdgeInsets(top: 0, left: 8, bottom: 0, right: -8)

          如果不知道alignmentRectInsets,可以先阅读this blog

      快捷版

      var customButton = UIButton(type: .custom)
      customButton.overrideAlignmentRectInsets = UIEdgeInsets(top: 0, left: x, bottom: 0, right: -x) // you should do this in your own custom class
      customButton.translatesAutoresizingMaskIntoConstraints = false;
      

      objective-c 版本

      UIButton *customButton = [UIButton buttonWithType:UIButtonTypeCustom];
      customButton.overrideAlignmentRectInsets = UIEdgeInsetsMake(0, x, 0, -x); // you should do this in your own custom class
      customButton.translatesAutoresizingMaskIntoConstraints = NO;
      

      2。使用您的自定义按钮创建一个项目

      快捷版

      var item = UIBarButtonItem(customView: customButton)
      

      objective-c 版本

      UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithCustomView:customButton]
      

      3。创建一个宽度为 positive

      的 fixedSpace 类型 UIBarButtonItem

      设置一个值,而不是负值

      快捷版

      var positiveSeparator = UIBarButtonItem(barButtonSystemItem:.fixedSpace, target: nil, action: nil)
      positiveSeparator.width = 8
      

      objective-c 版本

      UIBarButtonItem *positiveSeparator = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
      positiveSeparator.width = 8;
      

      4。将数组设置为 leftitems 或 rightitems

      fixedSpace 类型的 UIBarButton 项必须是数组中的第一个元素

      快捷版

      self.navigationItem.leftBarButtonItems = [positiveSeparator, item, ...]
      

      objective-c 版本

      self.navigationItem.leftBarButtonItems = @{positiveSeparator, item, ...}
      

      干得好

      完成所有步骤后,您会看到内边距变小并且输入区域似乎正确!

      如果您发现有问题,请告诉我! 我会尽力回答你的问题!

      注意

      在 iOS 11 之前,你应该关心设备的屏幕宽度;如果屏幕是5.5英寸,负片是-12pt,其他屏幕是-8pt。

      如果你在 iOS 11 上使用我的解决方案,你不需要关心设备屏幕,只需设置 8pt, 您应该关心项目在导航栏中的位置,左侧或右侧,这将影响您自定义视图的对齐矩形插入

      想要更多的点击区域

      如果你想保证你的点击区域大于 44 * 44 ,你可以覆盖下面的方法

      - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
          CGSize acturalSize = self.frame.size;
          CGSize minimumSize = kBarButtonMinimumTapAreaSize;
          CGFloat verticalMargin = acturalSize.height - minimumSize.height >= 0 ? 0 : ((minimumSize.height - acturalSize.height ) / 2);
          CGFloat horizontalMargin = acturalSize.width - minimumSize.width >= 0 ? 0 : ((minimumSize.width - acturalSize.width ) / 2);
          CGRect newArea = CGRectMake(self.bounds.origin.x - horizontalMargin, self.bounds.origin.y - verticalMargin, self.bounds.size.width + 2 * horizontalMargin, self.bounds.size.height + 2 * verticalMargin);
          return CGRectContainsPoint(newArea, point);
      }
      

      【讨论】:

      • 这对我来说非常有效,只是目标区域非常小,几乎无法使用。
      • @Eric 如果要修改点击区域,可以覆盖按钮的方法,如` - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event`
      • overrideAlignmentRectInsets 不是属性,alignmentRectInsets 仅可用于覆盖 - 请参阅我的答案
      • @Eric 如果你仔细阅读我的回答,你会发现我在 ios 11 章节的开头告诉你要覆盖alignmentRectInset
      • 我使用 customButton.contentEdgeInsets = UIEdgeInsets(top: 0, left: -25, bottom: 0, right: 0) 而不是 overrideAlignmentRectInsets。有了这个,我就不必使用 positiveSeparator。
      【解决方案6】:

      我认为一个棘手的方法是使用 Aspects(或方法调配),如下所示:

      [UINavigationBar aspect_hookSelector:@selector(layoutSubviews) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> info){
              UINavigationBar *bar = info.instance;
      
              //[bar layoutSubviews];
      
              if ([bar isKindOfClass:[UINavigationBar class]]) {
                  if (@available(iOS 11, *)) {
                      bar.layoutMargins = UIEdgeInsetsZero;
      
                      for (UIView *subview in bar.subviews) {
                          if ([NSStringFromClass([subview class]) containsString:@"ContentView"]) {
                              UIEdgeInsets oEdges = subview.layoutMargins;
                              subview.layoutMargins = UIEdgeInsetsMake(0, 0, 0, oEdges.right);
                          }
                      }
                  }
              }
          } error:nil];
      

      【讨论】:

        【解决方案7】:

        我在尝试将工具栏作为 UIButton 的子视图居中时偶然发现了这一点。由于工具栏需要小宽度,因此最好的解决方案是在每一侧设置一个灵活的按钮:

        UIBarButtonItem flexibleButtonItemLeft = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
        
        UIBarButtonItem centeredButtonItem = ...
        
        UIBarButtonItem flexibleButtonItemRight = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
        
        UIToolbar toolbar = ...
        
        toolbar.items = @[flexibleButtonItemLeft, centeredButtonItem, flexibleButtonItemRight];
        

        【讨论】:

          【解决方案8】:

          如果你想使用自定义视图(initWithCustomView),你可以改变你的工具栏的偏移和宽度

          [myToolBar setFrame:CGRectMake(-10, 0, [UIScreen mainScreen].bounds.size.width+10, 44)];
          

          【讨论】:

          • 我认为添加上面提到的 UIBarButtonSystemItemFixedSpace 是一个更简洁的解决方案 - 这样您就不会冒险让工具栏与 UI 中的其他控件重叠,从而导致其他问题。特别是如果工具栏是透明的。
          【解决方案9】:

          我遇到了同样的问题。最后,我通过继承 UIToolbar 并覆盖布局子视图方法解决了这个问题。

          - (void)layoutSubviews {
          
            [super layoutSubviews];
          
            if (leftItem_ && leftItem_.customView
                && [leftItem_.customView isKindOfClass:[UIButton class]]) {
              CGRect newFrame = leftItem_.customView.frame;
              newFrame.origin.x = 0;   // reset the original point x to 0; default is 12, wired number
              leftItem_.customView.frame = newFrame;    
            }
          
            if (rightItem_ && rightItem_.customView
                && [rightItem_.customView isKindOfClass:[UIButton class]]) {
              CGRect newFrame = rightItem_.customView.frame;
              newFrame.origin.x = self.frame.size.width - CGRectGetWidth(newFrame);
              rightItem_.customView.frame = newFrame;
            }
          
          }
          

          【讨论】:

          • 您是如何将自定义工具栏设置到当前导航控制器中的?
          【解决方案10】:

          最后,为了为 UIBarButtonItem 自定义背景图片并适应对齐,我放弃了 UIBarButtonItem 并手动添加 UIButton。

          UIImage *buttonImage = [[UIImage imageNamed:@"button.png"] stretchableImageWithLeftCapWidth:10 topCapHeight:0];
          UIButton *btnTest = [UIButton buttonWithType:UIButtonTypeCustom];
          [btnTest setBackgroundImage:buttonImage forState:UIControlStateNormal];
          [btnTest setTitle:@"Back" forState:UIControlStateNormal];   
          [btnTest.titleLabel setFont:[UIFont boldSystemFontOfSize:13]];  
          [btnTest setBackgroundImage:[imgToolbarButton stretchableImageWithLeftCapWidth:5 topCapHeight:0]  forState:UIControlStateNormal];
          [btnTest addTarget:self action:@selector(clearDateEdit:) forControlEvents:UIControlEventTouchUpInside];
          btnTest.frame = CGRectMake(0.0, 0.0, 50, 30);
          [self.toolbar addSubview:btnTest];
          [btnTestItem release];
          

          【讨论】:

            【解决方案11】:

            使用此按钮

            UIBarButtonItem *spaceItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
                                                                                       target:nil
                                                                                       action:nil];
            

            【讨论】:

              猜你喜欢
              • 2016-09-29
              • 1970-01-01
              • 2013-12-05
              • 2015-07-01
              • 1970-01-01
              • 2016-12-07
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多