【问题标题】:UIButton: how to center an image and a text using imageEdgeInsets and titleEdgeInsets?UIButton:如何使用 imageEdgeInsets 和 titleEdgeInsets 使图像和文本居中?
【发布时间】:2011-01-27 22:00:45
【问题描述】:

如果我只在按钮中放置一个图像并将 imageEdgeInsets 设置为更靠近顶部,则图像将保持居中并且一切都按预期工作:

[button setImage:image forState:UIControlStateNormal];
[button setImageEdgeInsets:UIEdgeInsetsMake(-15.0, 0.0, 0.0, 0.0)];

如果我只在按钮中放置一个文本并将 titleEdgeInsets 设置为更靠近底部,则文本将保持居中并且一切都按预期工作:

[button setTitle:title forState:UIControlStateNormal];
[button setTitleEdgeInsets:UIEdgeInsetsMake(0.0, 0.0, -30, 0.0)];

但是,如果我把 4 行放在一起,文字会干扰图像并且都失去了中心对齐。

我所有的图像都有 30 像素宽度,如果我在 setTitleEdgeInsets 的 UIEdgeInsetMake 的左侧参数中输入 30,则文本再次居中。问题是图像永远不会居中,因为它似乎取决于 button.titleLabel 的大小。我已经尝试了很多关于按钮大小、图像大小、titleLabel 大小的计算,但从来没有完全居中。

有人遇到过同样的问题吗?

【问题讨论】:

    标签: ios objective-c uibutton


    【解决方案1】:

    找到方法。

    首先,配置titleLabel的文字(因为样式,即粗体、斜体等)。然后,考虑到图像的宽度,使用setTitleEdgeInsets

    [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    [button setTitle:title forState:UIControlStateNormal];
    [button.titleLabel setFont:[UIFont boldSystemFontOfSize:10.0]];
    
    // Left inset is the negative of image width.
    [button setTitleEdgeInsets:UIEdgeInsetsMake(0.0, -image.size.width, -25.0, 0.0)]; 
    

    之后,考虑到文本边界的宽度,使用setTitleEdgeInsets

    [button setImage:image forState:UIControlStateNormal];
    
    // Right inset is the negative of text bounds width.
    [button setImageEdgeInsets:UIEdgeInsetsMake(-15.0, 0.0, 0.0, -button.titleLabel.bounds.size.width)];
    

    现在图像和文本将居中(在本例中,图像显示在文本上方)。

    干杯。

    【讨论】:

    • 7 年,伙计。真的不记得了。当我问的时候,我回答了自己(我的回答是当时唯一的一个)。当当前所选答案的作者专注于消除这些神奇数字时,我已经更改了所选答案。因此,在选定的答案中,您可以弄清楚它们的含义。
    【解决方案2】:

    对于它的价值,这里有一个通用的解决方案,可以将图像定位在文本上方的中心,而不使用任何幻数。 请注意,以下代码已过时,您可能应该使用以下更新版本之一

    // the space between the image and text
    CGFloat spacing = 6.0;
    
    // lower the text and push it left so it appears centered 
    //  below the image
    CGSize imageSize = button.imageView.frame.size;
    button.titleEdgeInsets = UIEdgeInsetsMake(
      0.0, - imageSize.width, - (imageSize.height + spacing), 0.0);
    
    // raise the image and push it right so it appears centered
    //  above the text
    CGSize titleSize = button.titleLabel.frame.size;
    button.imageEdgeInsets = UIEdgeInsetsMake(
      - (titleSize.height + spacing), 0.0, 0.0, - titleSize.width);
    

    以下版本包含支持 iOS 7+ 的更改,这些更改已在下面的 cmets 中推荐。我自己没有测试过这段代码,所以我不确定它的效果如何,或者如果在以前的 iOS 版本下使用它是否会崩溃。

    // the space between the image and text
    CGFloat spacing = 6.0;
    
    // lower the text and push it left so it appears centered 
    //  below the image
    CGSize imageSize = button.imageView.image.size;
    button.titleEdgeInsets = UIEdgeInsetsMake(
      0.0, - imageSize.width, - (imageSize.height + spacing), 0.0);
    
    // raise the image and push it right so it appears centered
    //  above the text
    CGSize titleSize = [button.titleLabel.text sizeWithAttributes:@{NSFontAttributeName: button.titleLabel.font}];
    button.imageEdgeInsets = UIEdgeInsetsMake(
      - (titleSize.height + spacing), 0.0, 0.0, - titleSize.width);
    
    // increase the content height to avoid clipping
    CGFloat edgeOffset = fabsf(titleSize.height - imageSize.height) / 2.0;
    button.contentEdgeInsets = UIEdgeInsetsMake(edgeOffset, 0.0, edgeOffset, 0.0);
    

    Swift 5.0 版本

    extension UIButton {
      func alignVertical(spacing: CGFloat = 6.0) {
        guard let imageSize = imageView?.image?.size,
          let text = titleLabel?.text,
          let font = titleLabel?.font
        else { return }
    
        titleEdgeInsets = UIEdgeInsets(
          top: 0.0,
          left: -imageSize.width,
          bottom: -(imageSize.height + spacing),
          right: 0.0
        )
    
        let titleSize = text.size(withAttributes: [.font: font])
        imageEdgeInsets = UIEdgeInsets(
          top: -(titleSize.height + spacing),
          left: 0.0,
          bottom: 0.0,
          right: -titleSize.width
        )
    
        let edgeOffset = abs(titleSize.height - imageSize.height) / 2.0
        contentEdgeInsets = UIEdgeInsets(
          top: edgeOffset,
          left: 0.0,
          bottom: edgeOffset,
          right: 0.0
        )
      }
    }
    

    【讨论】:

    • 太好了 - 谢谢!我认为很多其他答案都是关于这个“艰难的方式”——这看起来好多了。
    • 我发现上面的方法可以使用,但我完全没有关于如何操作的心智模型。任何人都有一个链接到一个图片解释各个 EdgeInsets 参数影响什么?为什么文本宽度会改变?
    • 当图像被缩小以适应按钮时,这不起作用。似乎 UIButton(至少在 iOS 7 中)使用 image.size,而不是 imageView.frame.size 来进行居中计算。
    • @Hemang 和 Dan Jackson,我正在采纳您的建议,而无需亲自进行测试。我发现我最初为 iOS 4 编写这个有点可笑,在这么多版本之后,我们仍然必须从本质上对 Apple 的布局算法进行逆向工程才能获得如此明显的功能。或者至少我认为从一致的支持流和下面同样骇人听闻的答案(无意侮辱)中,还没有更好的解决方案。
    • 在 iOS8 中,我发现使用 button.currentTitle 而不是 button.titleLabel.text 更好,尤其是在按钮文本会发生变化的情况下。 currentTitle 会立即填充,而 titleLabel.text 更改可能会很慢,这可能会导致插入不对齐。
    【解决方案3】:

    只要标签足够短而不会被截断,使用button.titleLabel.frame.size.width 就可以正常工作。当标签文本被截断时,定位不起作用。服用

    CGSize titleSize = [[[button titleLabel] text] sizeWithFont:[[button titleLabel] font]];
    

    即使标签文本被截断也对我有用。

    【讨论】:

    • 你的按钮打错了。
    【解决方案4】:

    我查看了现有答案,但我也发现设置按钮框架是重要的第一步。

    这是我使用的一个函数来处理这个问题:

    const CGFloat kImageTopOffset   = -15;
    const CGFloat kTextBottomOffset = -25;
    
    + (void) centerButtonImageTopAndTextBottom: (UIButton*)         button 
                                         frame: (CGRect)            buttonFrame
                                          text: (NSString*)         textString
                                     textColor: (UIColor*)          textColor
                                          font: (UIFont*)           textFont
                                         image: (UIImage*)          image
                                      forState: (UIControlState)    buttonState
    {
        button.frame = buttonFrame;
    
        [button setTitleColor: (UIColor*)       textColor
                     forState: (UIControlState) buttonState];
    
        [button setTitle: (NSString*) textString
                forState: (UIControlState) buttonState ];
    
    
        [button.titleLabel setFont: (UIFont*) textFont ];
    
        [button setTitleEdgeInsets: UIEdgeInsetsMake( 0.0, -image.size.width, kTextBottomOffset,  0.0)]; 
    
        [button setImage: (UIImage*)       image 
                forState: (UIControlState) buttonState ];
    
        [button setImageEdgeInsets: UIEdgeInsetsMake( kImageTopOffset, 0.0, 0.0,- button.titleLabel.bounds.size.width)];
    }
    

    【讨论】:

      【解决方案5】:

      这里有一些很好的例子,但在处理多行文本(文本换行)时,我无法让它在所有情况下都能正常工作。为了最终让它发挥作用,我结合了几种技术:

      1. 我使用了上面的 Jesse Crossen 示例。但是,我固定了文字高度 问题,我添加了指定水平文本边距的功能。 允许文本换行时,边距很有用,因此不会命中 按钮边缘:

        // the space between the image and text
        CGFloat spacing = 10.0;
        float   textMargin = 6;
        
        // get the size of the elements here for readability
        CGSize  imageSize   = picImage.size;
        CGSize  titleSize   = button.titleLabel.frame.size;
        CGFloat totalHeight = (imageSize.height + titleSize.height + spacing);      // get the height they will take up as a unit
        
        // lower the text and push it left to center it
        button.titleEdgeInsets = UIEdgeInsetsMake( 0.0, -imageSize.width +textMargin, - (totalHeight - titleSize.height), +textMargin );   // top, left, bottom, right
        
        // the text width might have changed (in case it was shortened before due to 
        // lack of space and isn't anymore now), so we get the frame size again
        titleSize = button.titleLabel.bounds.size;
        
        button.imageEdgeInsets = UIEdgeInsetsMake(-(titleSize.height + spacing), 0.0, 0.0, -titleSize.width );     // top, left, bottom, right        
        
      2. 确保将文本标签设置为换行

        button.titleLabel.numberOfLines = 2; 
        button.titleLabel.lineBreakMode = UILineBreakModeWordWrap;
        button.titleLabel.textAlignment = UITextAlignmentCenter;
        
      3. 现在大部分情况下都可以使用。但是,我有一些按钮无法正确呈现其图像。图像要么向右或向左移动(它没有居中)。所以我使用了 UIButton 布局覆盖技术来强制 imageView 居中。

        @interface CategoryButton : UIButton
        @end
        
        @implementation CategoryButton
        
        - (void)layoutSubviews
        {
            // Allow default layout, then center imageView
            [super layoutSubviews];
        
            UIImageView *imageView = [self imageView];
            CGRect imageFrame = imageView.frame;
            imageFrame.origin.x = (int)((self.frame.size.width - imageFrame.size.width)/ 2);
            imageView.frame = imageFrame;
        }
        @end
        

      【讨论】:

      • 似乎这是一个很好的解决方案,但是 button.titleLabel.numberOfLines 不应该为 0 以便它可以有尽可能多的行吗?
      • 就我而言,我只想要最多两行。否则,图像会有问题 w.r.t.按钮的整体大小。
      【解决方案6】:

      不要与系统对抗。如果您的布局变得过于复杂而无法使用 Interface Builder + 或者一些简单的配置代码进行管理,请使用 layoutSubviews 以更简单的方式手动进行布局 - 这就是它的用途!其他一切都将等同于黑客攻击。

      创建一个 UIButton 子类并覆盖它的 layoutSubviews 方法,以编程方式对齐您的文本和图像。或者使用 https://github.com/nickpaulson/BlockKit/blob/master/Source/UIView-BKAdditions.h 之类的东西,这样你就可以使用块来实现 layoutSubviews。

      【讨论】:

        【解决方案7】:

        我为@TodCunningham 的回答做了一个方法

         -(void) AlignTextAndImageOfButton:(UIButton *)button
         {
           CGFloat spacing = 2; // the amount of spacing to appear between image and title
           button.imageView.backgroundColor=[UIColor clearColor];
           button.titleLabel.lineBreakMode = UILineBreakModeWordWrap;
           button.titleLabel.textAlignment = UITextAlignmentCenter;
           // get the size of the elements here for readability
           CGSize imageSize = button.imageView.frame.size;
           CGSize titleSize = button.titleLabel.frame.size;
        
          // lower the text and push it left to center it
          button.titleEdgeInsets = UIEdgeInsetsMake(0.0, - imageSize.width, - (imageSize.height   + spacing), 0.0);
        
          // the text width might have changed (in case it was shortened before due to 
          // lack of space and isn't anymore now), so we get the frame size again
           titleSize = button.titleLabel.frame.size;
        
          // raise the image and push it right to center it
          button.imageEdgeInsets = UIEdgeInsetsMake(- (titleSize.height + spacing), 0.0, 0.0, -     titleSize.width);
         }
        

        【讨论】:

          【解决方案8】:

          或者你可以只使用这个类别:

          @interface UIButton (VerticalLayout)  
          
          - (void)centerVerticallyWithPadding:(float)padding;  
          - (void)centerVertically;  
          
          @end  
          
          
          @implementation UIButton (VerticalLayout)  
          
          - (void)centerVerticallyWithPadding:(float)padding 
          {      
              CGSize imageSize = self.imageView.frame.size;  
              CGSize titleSize = self.titleLabel.frame.size;  
          
              CGFloat totalHeight = (imageSize.height + titleSize.height + padding);  
          
              self.imageEdgeInsets = UIEdgeInsetsMake(- (totalHeight - imageSize.height),
                                                      0.0f,
                                                      0.0f,
                                                      - titleSize.width);
          
              self.titleEdgeInsets = UIEdgeInsetsMake(0.0f,
                                                      - imageSize.width,
                                                      - (totalHeight - titleSize.height),
                                                      0.0f);
          
          }
          
          
          - (void)centerVertically
          {  
              const CGFloat kDefaultPadding = 6.0f;
          
              [self centerVerticallyWithPadding:kDefaultPadding];  
          }  
          
          
          @end
          

          【讨论】:

          • 这不适用于自定义字体。来自 iOS7+,CGSize titleSize = [button.titleLabel.text sizeWithAttributes: @{NSFontAttributeName: button.titleLabel.font}];
          【解决方案9】:

          假设您希望文本和图像都水平居中,图像上方的文本: 将界面生成器中的文本居中并添加顶部插图(为图像腾出空间)。 (将左插图保留为 0)。使用界面生成器选择图像 - 它的实际位置将从代码中设置,所以不要担心在 IB 中看起来不正常。与上述其他答案不同,这实际上适用于所有当前支持的 ios 版本(5,6 和 7)。

          在代码中,只需在抓取图像后丢弃按钮的 ImageView(通过将按钮的图像设置为 null)(这也会自动将文本居中,必要时进行包装)。然后实例化自己的ImageView,帧大小和图片一样,放在中间。

          这样您仍然可以从界面生成器中选择图像(尽管它不会像在模拟器中那样在 IB 中对齐,但话又说回来,其他解决方案并不兼容所有支持的 ios 版本)

          【讨论】:

            【解决方案10】:

            您可以使用这个 Swift 扩展来做到这一点,它部分基于 Jesse Crossen 的回答:

            extension UIButton {
              func centerLabelVerticallyWithPadding(spacing:CGFloat) {
                // update positioning of image and title
                let imageSize = self.imageView.frame.size
                self.titleEdgeInsets = UIEdgeInsets(top:0,
                                                    left:-imageSize.width,
                                                    bottom:-(imageSize.height + spacing),
                                                    right:0)
                let titleSize = self.titleLabel.frame.size
                self.imageEdgeInsets = UIEdgeInsets(top:-(titleSize.height + spacing),
                                                    left:0,
                                                    bottom: 0,
                                                    right:-titleSize.width)
            
                // reset contentInset, so intrinsicContentSize() is still accurate
                let trueContentSize = CGRectUnion(self.titleLabel.frame, self.imageView.frame).size
                let oldContentSize = self.intrinsicContentSize()
                let heightDelta = trueContentSize.height - oldContentSize.height
                let widthDelta = trueContentSize.width - oldContentSize.width
                self.contentEdgeInsets = UIEdgeInsets(top:heightDelta/2.0,
                                                      left:widthDelta/2.0,
                                                      bottom:heightDelta/2.0,
                                                      right:widthDelta/2.0)
              }
            }
            

            这定义了一个函数centerLabelVerticallyWithPadding,它适当地设置标题和图像插入。

            它还设置 contentEdgeInsets,我认为这是确保intrinsicContentSize 仍然正常工作所必需的,这需要使用自动布局。

            我相信所有子类 UIButton 的解决方案在技术上都是非法的,因为你不应该继承 UIKit 控件。也就是说,理论上它们可能会在未来的版本中中断。

            【讨论】:

            • 在 iOS9 上测试。图片居中显示,但文字显示在左侧:(
            【解决方案11】:

            我的用例使 insets 无法管理:

            1. 按钮上的背景图片保持一致
            2. 动态文本和图像随着字符串长度和图像大小的变化而变化

            这就是我最终所做的,我对此非常满意:

            • 在情节提要上使用背景图像(带有模糊和颜色的圆形)创建按钮。

            • 在我的类中声明一个 UIImageView:

              @implementation BlahViewController {
                  UIImageView *_imageView;
              }
              
            • 在初始化时创建图像视图实例:

              -(id)initWithCoder:(NSCoder *)aDecoder {
                  self = [super initWithCoder:aDecoder];
                  if (self) {
                      _imageView = [[UIImageView alloc] initWithCoder:aDecoder];
                   }
                   return self;
               }
              
            • 在 viewDidLoad 中为我们的图像视图按钮添加一个新层并设置文本对齐方式:

              [self.btn addSubview:_imageView];
              [self.btn.titleLabel setTextAlignment:NSTextAlignmentCenter];
              
            • 在按钮单击方法中,将我选择的叠加图像添加到图像视图中,调整其大小以适合图像并将其居中放置在按钮中,但将其向上移动 15,以便我可以将文本偏移放在其下方:

              [_imageView setImage:[UIImage imageNamed:@"blahImageBlah]];
              [_imageView sizeToFit];
              _imageView.center = CGPointMake(ceilf(self.btn.bounds.size.width / 2.0f),
                       ceilf((self.btn.bounds.size.height / 2.0f) - 15));
              [self.btn setTitle:@"Some new text" forState:UIControlStateNormal];
              

            注意:ceilf() 对确保图像质量处于像素边界很重要。

            【讨论】:

            • 对于我的用例来说绝对是一种更好的方法,因为我正在将按钮添加到堆栈视图中。
            【解决方案12】:

            子类 UIButton

            - (void)layoutSubviews {
                [super layoutSubviews];
                CGFloat spacing = 6.0;
                CGSize imageSize = self.imageView.image.size;
                CGSize titleSize = [self.titleLabel sizeThatFits:CGSizeMake(self.frame.size.width, self.frame.size.height - (imageSize.height + spacing))];
                self.imageView.frame = CGRectMake((self.frame.size.width - imageSize.width)/2, (self.frame.size.height - (imageSize.height+spacing+titleSize.height))/2, imageSize.width, imageSize.height);
                self.titleLabel.frame = CGRectMake((self.frame.size.width - titleSize.width)/2, CGRectGetMaxY(self.imageView.frame)+spacing, titleSize.width, titleSize.height);
            }
            

            【讨论】:

              【解决方案13】:

              我很难完成这项工作,因为我无法在视图的构造函数上获取图像大小和文本宽度。 Jesse's answer 的两个小改动对我有用:

              CGFloat spacing = 3;
              self.titleEdgeInsets = UIEdgeInsetsMake(0.0, - image.size.width, - (image.size.height + spacing), 0.0);
              CGSize titleSize = [name sizeWithAttributes:@{NSFontAttributeName:self.titleLabel.font}];
              self.imageEdgeInsets = UIEdgeInsetsMake(- (titleSize.height + spacing), 0.0, 0.0, - titleSize.width);
              

              变化是:

              • 使用[NSString sizeWithAttributes]获取文字宽度;
              • 直接在UIImage而不是UIImageView上获取图像大小

              【讨论】:

                【解决方案14】:

                对 Jesse Crossen 的回答稍作改动,就让它对我来说很完美:

                代替:

                CGSize titleSize = button.titleLabel.frame.size;
                

                我用过这个:

                CGSize titleSize = [button.titleLabel.text sizeWithAttributes: @{NSFontAttributeName:button.titleLabel.font}];
                

                【讨论】:

                • 欢迎来到 SO!您可以直接将其写给 Jesse,而不是添加单独的答案(进行细微更改),以便他可以正确检查和更新他的 [已接受] 答案(如果需要)。
                【解决方案15】:

                为 Xcode 11+ 更新

                我的原始答案中描述的插图已在 Xcode 的较新版本中移至尺寸检查器。我不是 100% 清楚切换发生的时间,但如果在属性检查器中找不到插入信息,读者应该查看尺寸检查器。下面是新插入屏幕的示例(自 11.5 起位于尺寸属性检查器的顶部)。

                原答案

                其他答案没有错,但是我只是想指出,可以在 Xcode 中使用零行代码直观地完成相同的行为。如果您不需要计算值或使用情节提要/xib 构建(否则适用其他解决方案),此解决方案很有用。

                注意 - 我确实理解 OP 的问题是需要代码的问题。我只是为了完整性而提供这个答案,并作为使用故事板/xibs的人的逻辑替代方案。

                要使用边缘插图修改按钮的图像、标题和内容视图的间距,您可以选择按钮/控件并打开属性检查器。向下滚动到检查器的中间,找到 Edge insets 部分。

                还可以访问和修改标题、图像或内容视图的特定边缘插图。

                【讨论】:

                • 我不明白为什么我似乎无法在情节提要中为某些值输入负数。
                • 这适用于较新的 swift 版本吗?我在任何地方都找不到 Edge 属性
                • @brockhampton - 查看新位置的更新答案
                【解决方案16】:

                编辑:为 Swift 3 更新

                如果您正在寻找 Jesse Crossen 答案的 Swift 解决方案,您可以将其添加到 UIButton 的子类中:

                override func layoutSubviews() {
                
                    let spacing: CGFloat = 6.0
                
                    // lower the text and push it left so it appears centered
                    //  below the image
                    var titleEdgeInsets = UIEdgeInsets.zero
                    if let image = self.imageView?.image {
                        titleEdgeInsets.left = -image.size.width
                        titleEdgeInsets.bottom = -(image.size.height + spacing)
                    }
                    self.titleEdgeInsets = titleEdgeInsets
                
                    // raise the image and push it right so it appears centered
                    //  above the text
                    var imageEdgeInsets = UIEdgeInsets.zero
                    if let text = self.titleLabel?.text, let font = self.titleLabel?.font {
                        let attributes = [NSFontAttributeName: font]
                        let titleSize = text.size(attributes: attributes)
                        imageEdgeInsets.top = -(titleSize.height + spacing)
                        imageEdgeInsets.right = -titleSize.width
                    }
                    self.imageEdgeInsets = imageEdgeInsets
                
                    super.layoutSubviews()
                }
                

                【讨论】:

                  【解决方案17】:

                  这对我来说效果很好,适用于几个按钮,具有不同的图像宽度和不同的标题长度:

                  子类UIButton

                  override func layoutSubviews() {
                      super.layoutSubviews()
                  
                      if let image = imageView?.image {
                  
                          let margin = 30 - image.size.width / 2
                          let titleRect = titleRectForContentRect(bounds)
                          let titleOffset = (bounds.width - titleRect.width - image.size.width - margin) / 2
                  
                  
                          contentHorizontalAlignment = UIControlContentHorizontalAlignment.Left
                              imageEdgeInsets = UIEdgeInsetsMake(0, margin, 0, 0)
                              titleEdgeInsets = UIEdgeInsetsMake(0, (bounds.width - titleRect.width -  image.size.width - margin) / 2, 0, 0)
                      }
                  
                  }
                  

                  【讨论】:

                    【解决方案18】:

                    适用于 80x80 像素大小的按钮。

                    [self.leftButton setImageEdgeInsets:UIEdgeInsetsMake(0, 10.0, 20.0, 10.0)];    
                    [self.leftButton setTitleEdgeInsets:UIEdgeInsetsMake(60, -75.0, 0.0, 0.0)];
                    

                    【讨论】:

                      【解决方案19】:

                      我确实做了一些调整,使图像在水平中心对齐:

                      // the space between the image and text
                              let spacing = CGFloat(36.0);
                      
                              // lower the text and push it left so it appears centered
                              //  below the image
                              let imageSize = tutorialButton.imageView!.frame.size;
                              tutorialButton.titleEdgeInsets = UIEdgeInsetsMake(
                                  0, -CGFloat(imageSize.width), -CGFloat(imageSize.height + spacing), 0.0);
                      
                              // raise the image and push it right so it appears centered
                              //  above the text
                              let titleSize = tutorialButton.titleLabel!.frame.size;
                              tutorialButton.imageEdgeInsets = UIEdgeInsetsMake(
                                  -CGFloat(titleSize.height + spacing), CGFloat((tutorialButton.frame.width - imageSize.width) / 2), 0.0, -CGFloat(titleSize.width));
                      

                      【讨论】:

                        【解决方案20】:

                        有了这段代码,你会得到类似的东西

                        extension UIButton {
                            func alignTextUnderImage() {
                                guard let imageView = imageView else {
                                        return
                                }
                                self.contentVerticalAlignment = .Top
                                self.contentHorizontalAlignment = .Center
                                let imageLeftOffset = (CGRectGetWidth(self.bounds) - CGRectGetWidth(imageView.bounds)) / 2//put image in center
                                let titleTopOffset = CGRectGetHeight(imageView.bounds) + 5
                                self.imageEdgeInsets = UIEdgeInsetsMake(0, imageLeftOffset, 0, 0)
                                self.titleEdgeInsets = UIEdgeInsetsMake(titleTopOffset, -CGRectGetWidth(imageView.bounds), 0, 0)
                            }
                        }
                        

                        【讨论】:

                        【解决方案21】:

                        是否必须使用边缘插图?如果没有,您可以尝试将尊重定位到中心父视图

                        extension UIButton 
                        {
                            func centerImageAndTextVerticaAlignment(spacing: CGFloat) 
                            {
                                var titlePoint : CGPoint = convertPoint(center, fromView:superview)
                                var imageViewPoint : CGPoint = convertPoint(center, fromView:superview)
                                titlePoint.y += ((titleLabel?.size.height)! + spacing)/2
                                imageViewPoint.y -= ((imageView?.size.height)! + spacing)/2
                                titleLabel?.center = titlePoint
                                imageView?.center = imageViewPoint
                        
                            }
                        }
                        

                        【讨论】:

                        • 这个问题明确要求使用 imageEdgeInsets 和 titleEdgeInsets 所以它很可能是强制性的
                        【解决方案22】:

                        使用 Swift 3+ 语法的 UIButton 扩展:

                        extension UIButton {
                            func alignImageAndTitleVertically(padding: CGFloat = 6.0) {
                                let imageSize: CGSize = imageView!.image!.size
                                titleEdgeInsets = UIEdgeInsetsMake(0.0, -imageSize.width, -(imageSize.height + padding), 0.0)
                                let labelString = NSString(string: titleLabel!.text!)
                                let titleSize = labelString.size(attributes: [NSFontAttributeName: titleLabel!.font])
                                self.imageEdgeInsets = UIEdgeInsetsMake(-(titleSize.height + padding), 0.0, 0.0, -titleSize.width)
                                let edgeOffset = abs(titleSize.height - imageSize.height) / 2.0;
                                self.contentEdgeInsets = UIEdgeInsetsMake(edgeOffset, 0.0, edgeOffset, 0.0)
                            }
                        }
                        

                        原答案:https://stackoverflow.com/a/7199529/3659227

                        【讨论】:

                          【解决方案23】:

                          您需要将图像向右移动文本的宽度。然后将文本向左移动图像的宽度。

                          UIEdgeInsets imageEdgeInsets = self.remoteCommandsButtonLights.imageEdgeInsets;
                          imageEdgeInsets.left = [button.titleLabel.text sizeWithAttributes:@{NSFontAttributeName:[button.titleLabel font]}].width;
                          imageEdgeInsets.bottom = 14.0;
                          button.imageEdgeInsets = imageEdgeInsets;
                          
                          UIEdgeInsets titleEdgeInsets = self.remoteCommandsButtonLights.titleEdgeInsets;
                          titleEdgeInsets.left = -button.currentImage.size.width;
                          titleEdgeInsets.top = 20.0;
                          button.titleEdgeInsets = titleEdgeInsets;
                          

                          然后调整顶部和底部插图以调整 Y 轴。这也可以通过编程方式完成,但对于您的图像大小应该是恒定的。而 X 轴插图需要根据每个按钮中文本标签的大小进行更改。

                          【讨论】:

                            【解决方案24】:

                            Jesse Crossen 更新了 Swift 4 的答案:

                            extension UIButton {
                                func alignVertical(spacing: CGFloat = 6.0) {
                                    guard let imageSize = self.imageView?.image?.size,
                                        let text = self.titleLabel?.text,
                                        let font = self.titleLabel?.font
                                        else { return }
                                    self.titleEdgeInsets = UIEdgeInsets(top: 0.0, left: -imageSize.width, bottom: -(imageSize.height + spacing), right: 0.0)
                                    let labelString = NSString(string: text)
                                    let titleSize = labelString.size(withAttributes: [kCTFontAttributeName as NSAttributedStringKey: font])
                                    self.imageEdgeInsets = UIEdgeInsets(top: -(titleSize.height + spacing), left: 0.0, bottom: 0.0, right: -titleSize.width)
                                    let edgeOffset = abs(titleSize.height - imageSize.height) / 2.0;
                                    self.contentEdgeInsets = UIEdgeInsets(top: edgeOffset, left: 0.0, bottom: edgeOffset, right: 0.0)
                                }
                            }
                            

                            这样使用:

                            override func viewDidLayoutSubviews() {
                                button.alignVertical()
                            }
                            

                            【讨论】:

                            • 很多很多小时之后。这是唯一有效的方法:)
                            • 嗨,Harry,很高兴听到这个消息。任何与 iOS 相关的内容都可以随时提问。分享就是关怀!
                            【解决方案25】:

                            在扩展 Swift 4.2 中添加此代码

                             func moveImageLeftTextCenter(imagePadding: CGFloat = 30.0){
                                guard let imageViewWidth = self.imageView?.frame.width else{return}
                                guard let titleLabelWidth = self.titleLabel?.intrinsicContentSize.width else{return}
                                self.contentHorizontalAlignment = .left
                                imageEdgeInsets = UIEdgeInsets(top: 0.0, left: imagePadding - imageViewWidth / 2, bottom: 0.0, right: 0.0)
                                titleEdgeInsets = UIEdgeInsets(top: 0.0, left: (bounds.width - titleLabelWidth) / 2 - imageViewWidth, bottom: 0.0, right: 0.0)
                            }
                            func moveImageRIghtTextCenter(imagePadding: CGFloat = 30.0){
                                guard let imageViewWidth = self.imageView?.frame.width else{return}
                                guard let titleLabelWidth = self.titleLabel?.intrinsicContentSize.width else{return}
                                self.contentHorizontalAlignment = .right
                                imageEdgeInsets = UIEdgeInsets(top: 0.0, left:0.0 , bottom: 0.0, right: imagePadding - imageViewWidth / 2)
                                titleEdgeInsets = UIEdgeInsets(top: 0.0, left:0.0 , bottom: 0.0, right:(bounds.width - titleLabelWidth) / 2 - imageViewWidth)
                            }
                            

                            【讨论】:

                              【解决方案26】:

                              只需投入我的 2 美分,这对我有用:

                              extension UIButton {
                                public func centerImageAndTextVertically(spacing: CGFloat) {
                                  layoutIfNeeded()
                                  let contentFrame = contentRect(forBounds: bounds)
                                  let imageFrame = imageRect(forContentRect: contentFrame)
                                  let imageLeftInset = bounds.size.width * 0.5 - imageFrame.size.width * 0.5
                                  let imageTopInset = -(imageFrame.size.height + spacing * 0.5)
                                  let titleFrame = titleRect(forContentRect: contentFrame)
                                  let titleLeftInset = ((bounds.size.width - titleFrame.size.width) * 0.5) - imageFrame.size.width
                                  let titleTopInmset = titleFrame.size.height + spacing * 0.5
                                  imageEdgeInsets = UIEdgeInsets(top: imageTopInset, left: imageLeftInset, bottom: 0, right: 0)
                                  titleEdgeInsets = UIEdgeInsets(top: titleTopInmset, left: titleLeftInset, bottom: 0, right: 0)
                                }
                              }
                              

                              【讨论】:

                                猜你喜欢
                                • 1970-01-01
                                • 2011-06-01
                                • 2013-03-04
                                • 1970-01-01
                                • 2017-05-23
                                • 1970-01-01
                                • 2020-10-11
                                • 1970-01-01
                                • 2012-06-12
                                相关资源
                                最近更新 更多