【问题标题】:iOS: UIButton resize according to text lengthiOS:UIButton根据文本长度调整大小
【发布时间】:2011-05-07 07:24:31
【问题描述】:

在界面生成器中,按住 Command + = 将调整按钮的大小以适合其文本。我想知道在将按钮添加到视图之前是否可以通过编程方式执行此操作。

UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button.titleLabel setFont:[UIFont fontWithName:@"Arial-BoldMT" size:12]];
[button addTarget:self action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
// I need to know the width needed to accomodate NSStringVariable
[button setTitle:NSStringVariable forState:UIControlStateNormal]; 
// So that I can set the width property of the button before addSubview
[button setFrame:CGRectMake(10, 0, width, fixedHeight)];
[subNavigation addSubview:button];

【问题讨论】:

  • 注意这个非常古老的问题: 如今(2017 年),只需将约束设置为“大于或等于”即可。 (请注意下面 Tad 的回答。)
  • @Fattie “大于或等于”约束不足以与 AutoLayout 兼容。如今(2017 年以上),应该将按钮包装在可以正确调整大小的东西中(Bartłomiej Semańczyk 回答),或者应该将 UIButton 子类化,如stackoverflow.com/a/50575588/1033581
  • 嗨@Cœur - 自动布局有很多微妙之处。您可能正在考虑稍微不同的情况?但是,幸运的是,您完全错了 - 尝试一下!只需将 UIButton 放在视图控制器上。显然设置了水平和垂直中心。然后添加 >= 100 宽度约束。它在最新的 iOS 中完美运行。
  • (对于任何不熟悉自动布局的人在这里搜索......只是待定,您根本不需要添加任何东西,并且 UIButton(还有 UILabel 等)将调整大小随着文本的变化自动调整宽度。只需正确限制 H/V 位置。)
  • @Fattie demonstration。特别是在运行时,它的行为是这样的:github.com/Coeur/autolayoutButton/blob/master/buttonSelfSizing/…

标签: ios uibutton


【解决方案1】:

因为sizeToFit 将忽略titleEdgeInsets 设置。在我的例子中,按钮标题是固定文本,所以我使用自动布局给每个按钮一个宽度约束。

而且我认为如果是动态文本长度,你需要先获取字符串大小并更新viewDidLayoutSubview中的按钮约束。

    lazy var importButton: UIButton = {
        let btn = UIButton()
        btn.translatesAutoresizingMaskIntoConstraints = false
        btn.setTitle("Import", for: .normal)
        btn.titleLabel?.font = .systemFont(ofSize: 18, weight: .bold)
        btn.setTitleColor(.white, for: .normal)
        btn.titleEdgeInsets = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
        btn.backgroundColor = .myBlue
        btn.layer.cornerRadius = 8
        btn.clipsToBounds = true
        btn.addTarget(self, action: #selector(importButtonTapped), for: .touchUpInside)
        return btn
    }()
    
    lazy var stitchButton: UIButton = {
        let btn = UIButton()
        btn.translatesAutoresizingMaskIntoConstraints = false
        btn.setTitle("Stitch", for: .normal)
        btn.titleLabel?.font = .systemFont(ofSize: 18, weight: .bold)
        btn.setTitleColor(.white, for: .normal)
        btn.titleEdgeInsets = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
        btn.backgroundColor = .myGreen
        btn.layer.cornerRadius = 8
        btn.clipsToBounds = true
        btn.addTarget(self, action: #selector(stitchButtonTapped), for: .touchUpInside)
        return btn
    }()

    func setViews() {
        title = "Image Stitcher"
        view.backgroundColor = .systemBackground
        
        view.addSubview(importButton)
        view.addSubview(stitchButton)

        let g = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            NSLayoutConstraint(item: importButton, attribute: .centerX, relatedBy: .equal, toItem: g, attribute: .trailing, multiplier: 0.3, constant: 0),
            importButton.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -50),
            importButton.widthAnchor.constraint(equalTo: g.widthAnchor, multiplier: 0.25),
            importButton.heightAnchor.constraint(equalTo: importButton.widthAnchor, multiplier: 0.5),
            
            NSLayoutConstraint(item: stitchButton, attribute: .centerX, relatedBy: .equal, toItem: g, attribute: .trailing, multiplier: 0.7, constant: 0),
            stitchButton.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -50),
            stitchButton.widthAnchor.constraint(equalTo: g.widthAnchor, multiplier: 0.25),
            stitchButton.heightAnchor.constraint(equalTo: stitchButton.widthAnchor, multiplier: 0.5),
        ])
    }

【讨论】:

    【解决方案2】:

    出于某种原因,func sizeToFit() 不适合我。我的设置是我在UITableViewCell 中使用一个按钮,并且我正在使用自动布局。

    对我有用的是:

    1. 获取宽度约束
    2. 获取intrinsicContentSize 宽度,因为根据此document 自动布局不知道intrinsicContentSize。将宽度约束设置为intrinsicContentSize宽度

    这是来自Debug View Hierachry 的两个标题

    【讨论】:

      【解决方案3】:

      说实话,我认为情节提要中没有简单的复选框来表示您想要调整按钮大小以适应文本,这真的很遗憾。嗯……随便。

      这是使用情节提要的最简单的解决方案。

      1. 放置 UIView 并为其设置约束。例子:
      2. 将 UILabel 放在 UIView 中。设置约束以将其附加到 UIView 的边缘。

      3. 将您的 UIButton 放在 UIView 中。设置相同的约束将其附加到 UIView 的边缘。

      4. 将 UILabel 的行数设置为 0。

      5. 设置插座。

          @IBOutlet var button: UIButton!
          @IBOutlet var textOnTheButton: UILabel!
      
      1. 获取一些很长很长的标题。
      
          let someTitle = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
      
      1. 在 viewDidLoad 上为 UILabel 和 UIButton 设置标题。
      
          override func viewDidLoad() {
              super.viewDidLoad()
              textOnTheButton.text = someTitle
              button.setTitle(someTitle, for: .normal)
              button.titleLabel?.numberOfLines = 0
          }
      
      1. 运行它以确保该按钮已调整大小并且可以按下。

      【讨论】:

        【解决方案4】:

        斯威夫特 4.2

        感谢上帝,这解决了。为按钮设置文本后,您可以从 UIView 中检索到作为自然大小的 intrinsicContentSize(官方文档为here)。 对于 UIButton,您可以像下面这样使用它。

        button.intrinsicContentSize.width
        

        为了您的信息,我调整了宽度以使其看起来正常。

        button.frame = CGRect(fx: xOffset, y: 0.0, width: button.intrinsicContentSize.width + 18, height: 40)
        

        模拟器

        UIButtons with intrinsicContentSize

        来源:https://riptutorial.com/ios/example/16418/get-uibutton-s-size-strictly-based-on-its-text-and-font

        【讨论】:

        • 您能否提供更多背景信息?您的代码行与问题中的代码有何关系?您能否总结一下您建议的代码的作用?仅提供链接作为解释不是本网站的正确格式(请参阅meta.stackexchange.com/a/8259/266197 了解原因),但如果您添加一些解释和上下文,您的答案将变得更有价值!
        【解决方案5】:

        在 UIKit 中,添加了 NSString 类以从给定的 NSString 对象获取以某种字体呈现时它将占用的大小。

        文档是here。现在它是 here 已弃用。

        简而言之,如果你去:

        CGSize stringsize = [myString sizeWithFont:[UIFont systemFontOfSize:14]]; 
        //or whatever font you're using
        [button setFrame:CGRectMake(10,0,stringsize.width, stringsize.height)];
        

        ...您将按钮的框架设置为您正在呈现的字符串的高度和宽度。

        您可能希望在该 CGSize 周围试验一些缓冲区空间,但您将从正确的地方开始。

        【讨论】:

        • 看起来 iOS 默认填充是 12px、8px。
        • sizeWithFont 在 iOS 7.0 中已弃用。使用 sizeWithAttributes: 代替
        • 我强烈建议不要再玩帧,而只能使用约束。
        • @GilSand 有没有办法为此使用界面生成器?
        • 是的,谷歌搜索应该会给你很多有用的结果
        【解决方案6】:

        sizeToFit 无法正常工作。而是:

        myButton.size = myButton.sizeThatFits(CGSize.zero)
        

        您也可以在按钮上添加contentInset

        myButton.contentEdgeInsets = UIEdgeInsetsMake(8, 8, 4, 8)

        【讨论】:

        • 来自 Apple 文档关于 sizeToFit: // 调用 sizeThatFits: 与当前视图边界并更改边界大小。所以它可以正常工作,你只需要在设置文本之后设置它。
        【解决方案7】:

        此按钮类具有自动调整文本高度的功能(适用于 Xamarin,但可以重写为其他语言)

        [Register(nameof(ResizableButton))]
        public class ResizableButton : UIButton
        {
            NSLayoutConstraint _heightConstraint;
            public bool NeedsUpdateHeightConstraint { get; private set; } = true;
        
            public ResizableButton(){}
        
            public ResizableButton(UIButtonType type) : base(type){}
        
            public ResizableButton(NSCoder coder) : base(coder){}
        
            public ResizableButton(CGRect frame) : base(frame){}
        
            protected ResizableButton(NSObjectFlag t) : base(t){}
        
            protected internal ResizableButton(IntPtr handle) : base(handle){}
        
            public override void LayoutSubviews()
            {
                base.LayoutSubviews();
                UpdateHeightConstraint();
                InvalidateIntrinsicContentSize();
            }
        
            public override void SetTitle(string title, UIControlState forState)
            {
                NeedsUpdateHeightConstraint = true;
                base.SetTitle(title, forState);
            }
        
            private void UpdateHeightConstraint()
            {
                if (!NeedsUpdateHeightConstraint)
                    return;
                NeedsUpdateHeightConstraint = false;
                var labelSize = TitleLabel.SizeThatFits(new CGSize(Frame.Width - TitleEdgeInsets.Left - TitleEdgeInsets.Right, float.MaxValue));
        
                var rect = new CGRect(Frame.X, Frame.Y, Frame.Width, labelSize.Height + TitleEdgeInsets.Top + TitleEdgeInsets.Bottom);
        
                if (_heightConstraint != null)
                    RemoveConstraint(_heightConstraint);
        
                _heightConstraint = NSLayoutConstraint.Create(this, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1, rect.Height);
                AddConstraint(_heightConstraint);
            }
        
             public override CGSize IntrinsicContentSize
             {
                 get
                 {
                     var labelSize = TitleLabel.SizeThatFits(new CGSize(Frame.Width - TitleEdgeInsets.Left - TitleEdgeInsets.Right, float.MaxValue));
                     return new CGSize(Frame.Width, labelSize.Height + TitleEdgeInsets.Top + TitleEdgeInsets.Bottom);
                 }
             }
        }
        

        【讨论】:

          【解决方案8】:

          要使用自动布局完成此操作,请尝试设置可变宽度约束:

          您可能还需要调整 Content Hugging PriorityContent Compression Resistance Priority 以获得所需的结果。


          UILabel 完全自动调整大小

          这个 UILabel 被简单地设置为在屏幕上居中(只有两个约束,水平/垂直):

          它完全自动改变宽度:

          您无需设置任何宽度或高度 - 这是完全自动的。

          请注意,黄色小方块只是简单地附加在一起(“间距”为零)。它们会随着 UILabel 调整大小而自动移动。

          添加 ">=" 约束设置 UILabel 的最小宽度:

          【讨论】:

            【解决方案9】:

            在 Xcode 4.5 及更高版本中,现在可以使用“Auto-layouting / Constraints”来完成。

            主要优点是:

            1. 您根本不需要以编程方式设置框架!
            2. 如果操作正确,您无需为改变方向而重新设置框架。
            3. 此外,设备更改不必打扰您(阅读,无需针对不同的屏幕尺寸单独编码)。

            一些缺点:

            1. 不向后兼容 - 仅适用于 iOS 6 及更高版本。
            2. 需要熟悉一下(但以后会节省时间)。

            最酷的事情是我们可以专注于声明一个意图,例如:

            • 我希望这两个按钮的宽度相同;或
            • 我需要这个视图垂直居中并从超级视图的边缘延伸到最大 10 pts;甚至,
            • 我希望此按钮/标签根据其显示的标签调整大小!

            Here 是介绍自动布局的简单教程。

            对于more details

            一开始需要一些时间,但看起来确实值得付出努力。

            【讨论】:

            • 我差点错过了这个答案——它真的需要更多的赞成票。使用此解决方案可以避免嵌入字体名称和大小或仅适用于 iOS 7 的调用。我只是设置了我希望 UIButton 扩展的一侧的约束,就是这样。
            • 几乎是一个完美的解决方案,不幸的是如果你使用titleEdgeInsets它不起作用:/
            • 这是一个糟糕的答案——它详细说明了 AutoLayout 本身的优点,而不是在这种情况下如何应用 AutoLayout 来解决提问者的问题。
            • 我想知道这是否可以通过编程方式实现 答案是……
            • @JuanPabloBoeroAlvarez 自动布局约束可以在 Interface Builder 中和/或以编程方式指定。
            【解决方案10】:

            斯威夫特:

            这里重要的是什么时候你会调用.sizeToFit(),它应该在设置文本显示之后,以便它可以读取所需的大小,如果你以后更新文本,然后调用它再次。

            var button = UIButton()
            button.setTitle("Length of this text determines size", forState: UIControlState.Normal)
            button.sizeToFit()
            self.view.addSubview(button)
            

            【讨论】:

              【解决方案11】:

              如果你想调整文本而不是按钮的大小,你可以使用 ...

              button.titleLabel.adjustsFontSizeToFitWidth = YES;
              button.titleLabel.minimumScaleFactor = .5;
              // The .5 value means that I will let it go down to half the original font size 
              // before the texts gets truncated
              
              // note, if using anything pre ios6, you would use I think
              button.titleLabel.minimumFontSize = 8;
              

              【讨论】:

              • * 调整FontSizeToFitWidth
              【解决方案12】:

              如果您的按钮是使用 Interface Builder 制作的,并且您正在更改代码中的标题,您可以这样做:

              [self.button setTitle:@"Button Title" forState:UIControlStateNormal];
              [self.button sizeToFit];
              

              【讨论】:

              • 即使您的按钮是用代码制作的,您也可以这样做。
              【解决方案13】:

              简单地说:

              1. 创建UIView 作为包装器,自动布局到周围的视图。
              2. UILabel 放入该包装器中。添加将标签粘贴到包装边缘的约束。
              3. UIButton 放入包装器中,然后简单地添加与UILabel 相同的约束。
              4. 享受自动调整大小的按钮和文本。

              【讨论】:

              • 这是否与可访问性配合得很好?
              【解决方案14】:

              我有一些与此帖子相关的额外需求,sizeToFit 无法解决。无论文本如何,我都需要将按钮保持在其原始位置的中心。我也不希望按钮大于其原始大小。

              因此,我将按钮设置为最大尺寸,将该尺寸保存在控制器中,并在文本更改时使用以下方法调整按钮的大小:

              + (void) sizeButtonToText:(UIButton *)button availableSize:(CGSize)availableSize padding:(UIEdgeInsets)padding {    
              
                   CGRect boundsForText = button.frame;
              
                  // Measures string
                  CGSize stringSize = [button.titleLabel.text sizeWithFont:button.titleLabel.font];
                  stringSize.width = MIN(stringSize.width + padding.left + padding.right, availableSize.width);
              
                  // Center's location of button
                  boundsForText.origin.x += (boundsForText.size.width - stringSize.width) / 2;
                  boundsForText.size.width = stringSize.width;
                  [button setFrame:boundsForText];
               }
              

              【讨论】:

                【解决方案15】:

                在代码中这样做的方法是:

                [button sizeToFit];
                

                如果您是子类化并且想要添加额外的规则,您可以覆盖:

                - (CGSize)sizeThatFits:(CGSize)size;
                

                【讨论】:

                • sizeToFit 在 UIButton 或 UILabel 等标准控件上完美运行。如果您在自定义 UIView 上执行此操作,则需要实现 -(CGSize)sizeThatFits:(CGSize)size
                • 只有在设置文本后放上才有效,否则没有足够的信息说明它应该多大:[button setTitle:@"Your text here" forState:UIControlStateNormal];[button sizeToFit];另外如果你稍后更新文本,不要忘记再次调用它。
                • 确保您仅以这种方式设置文本:[myButton setTitle:@"my title" forState:UIControlStateNormal];对我来说,直到我重复那句话才起作用。
                • sizeToFit() 不尊重标题边缘插入。
                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2011-01-26
                • 2013-04-10
                • 1970-01-01
                • 2015-08-28
                • 2021-12-11
                相关资源
                最近更新 更多