IOS / Objective-C:以编程方式在按钮下的中心线
Posted
技术标签:
【中文标题】IOS / Objective-C:以编程方式在按钮下的中心线【英文标题】:IOS/Objective-C: Center Line under Button Programmatically 【发布时间】:2018-08-28 16:22:56 【问题描述】:我正在尝试在三个按钮下做一条简单的线来创建标签效果。虽然我可以在 Storyboard 中执行此操作,但我想在代码中执行此操作,并且理想情况下使该行成为视图中已有元素的子视图,以避免必须对其进行特殊约束。
我的方法是创建一个没有文本和背景颜色的 UILabel。我可以使 UILabel 成为 View 的子视图,但是,它不会将其附加到按钮的底部。
另一方面,如果我将 UILabel 设为其中一个按钮的子视图,我将看不到它。
谁能提出一个简单的方法来做到这一点?
[centerButton setTitle:@"Favorites" forState:UIControlStateNormal];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(20, 120, 280, 2)];
label.backgroundColor = [UIColor redColor];
label.textAlignment = NSTextAlignmentCenter;
label.numberOfLines = 1;
label.text = @"";
[self.view addSubview: label];
提前感谢您的任何建议。
编辑:
我尝试以编程方式添加一些 NSLayoutConstraints:
NSLayoutConstraint *con3 = [NSLayoutConstraint
constraintWithItem:label attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual toItem:centerButton
attribute:NSLayoutAttributeBottom multiplier:1 constant:0];
[label addConstraints:@[con3]];
//tried with and without
[self.view layoutIfNeeded];
这返回了异常:
[LayoutConstraints] The view hierarchy is not prepared
for the constraint: <NSLayoutConstraint:0x174e85500 V:
[UIButton:0x100b0baf0'Now']-(0)-[UILabel:0x108555160]
(inactive)>
When added to a view, the constraint's items
must be descendants of that view (or the view itself).
This will crash if the constraint needs to be resolved
before the view hierarchy is assembled.
Break on -[UIView(UIConstraintBasedLayout)
_viewHierarchyUnpreparedForConstraint:] to debug.
在故事板中创建的按钮是 self.view 的子视图,我将标签添加到 self.view 作为代码中的子视图,因此不确定为什么会发生异常。
【问题讨论】:
对于这种事情,框架数学在这一点上是相当老派的。我肯定会尝试约束。 视图非常复杂,有很多故事板自动布局约束。只是希望将线分配给已经受到约束的东西,以免重做很多现有的约束 您想要从一个“标签按钮”到选定的“动画”行吗?还是您只想显示一条线? 或者,您可以尝试UISegmentedControl
子类并调整其外观。它会处理布局本身。
使用约束会更容易实现你想要做的事情。
【参考方案1】:
我从错误消息中想知道您是否可能在将 redLabel 视图添加到它的 parentView 之前尝试添加 NSLayoutConstraint(或在生命周期中太早)。无论如何,我认为这非常接近您想要完成的目标:
#import "ViewController.h"
@interface ViewController ()
@property (strong, nullable) UILabel *previousRedLabel;
@end
@implementation ViewController
- (void)viewDidLoad
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
- (IBAction)buttonClicked:(id)sender
if ([sender isKindOfClass:[UIButton class]])
UIButton *clickedButton = (UIButton *)sender;
UIView *buttonSuperview = [clickedButton superview];
if (buttonSuperview != nil)
[self _putRedLineWithHeight:3.0f atTheBottomOfView:buttonSuperview animate:YES];
- (void)_putRedLineWithHeight:(CGFloat)height atTheBottomOfView:(UIView *)viewToPutUnder animate:(BOOL)animate
// remove our previous red line
if (self.previousRedLabel)
// if you want it to be a no-op here if they click the same button
// you'll need to add some logic to check if the superView == viewToPutUnder
[self.previousRedLabel removeFromSuperview];
self.previousRedLabel = nil;
UILabel *redLabel = [[UILabel alloc] init];
// we're using autolayout so we don't want any resizing from it
redLabel.translatesAutoresizingMaskIntoConstraints = NO;
redLabel.backgroundColor = [UIColor redColor];
// start out with alpha = 0
redLabel.alpha = 0.0f;
// add it to our parentView
[viewToPutUnder addSubview:redLabel];
// height (determined by passed in value)
NSAssert(height >= 0, @"Height must be a positive number");
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:redLabel attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:height];
// width equal to parentView's width
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:viewToPutUnder attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:redLabel attribute:NSLayoutAttributeWidth multiplier:1.0f constant:0.0f];
// center x == parentView's center x
NSLayoutConstraint *centerConstraint = [NSLayoutConstraint constraintWithItem:viewToPutUnder attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:redLabel attribute:NSLayoutAttributeCenterX multiplier:1.0f constant:0.0f];
// now the bottom constraint (place it at the bottom of the parent view)
NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint constraintWithItem:viewToPutUnder attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:redLabel attribute:NSLayoutAttributeBottom multiplier:1.0f constant:0.0f];
// add the height constraint to our label
[redLabel addConstraint:heightConstraint];
// and all the other constraints to our parent view
[viewToPutUnder addConstraints:@[widthConstraint, centerConstraint, bottomConstraint]];
redLabel.alpha = 1.0f;
if (animate)
[UIView animateWithDuration:0.6f animations:^
[redLabel layoutIfNeeded];
];
self.previousRedLabel = redLabel;
动画示例:
还有一个非动画:
编辑答案以处理每个按钮不在其自己的视图中的情况
如果所有按钮都在一个超级视图中(宽度基于按钮宽度,中心到按钮中心,并将标签顶部固定到按钮底部)进行调整
#import "ViewController.h"
@interface ViewController ()
@property (strong, nullable) UILabel *previousRedLabel;
- (void)_putRedLineWithHeight:(CGFloat)height atTheBottomOfButton:(UIButton *)button animate:(BOOL)animate;
@end
@implementation ViewController
- (void)viewDidLoad
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
- (IBAction)buttonClicked:(id)sender
if ([sender isKindOfClass:[UIButton class]])
UIButton *clickedButton = (UIButton *)sender;
// if you want it to be a no-op here if they click the same button
// you'll need to add some logic to store the previous clicked button and check whether it's the same button
[self _putRedLineWithHeight:3.0f atTheBottomOfButton:clickedButton animate:YES];
- (void)_putRedLineWithHeight:(CGFloat)height atTheBottomOfButton:(UIButton *)button animate:(BOOL)animate
UIView *buttonSuperview = button.superview;
NSAssert(buttonSuperview != nil, @"Button has to have a superview");
// remove our previous red line
if (self.previousRedLabel)
[self.previousRedLabel removeFromSuperview];
self.previousRedLabel = nil;
UILabel *redLabel = [[UILabel alloc] init];
// we're using autolayout so we don't want any resizing from it
redLabel.translatesAutoresizingMaskIntoConstraints = NO;
redLabel.backgroundColor = [UIColor redColor];
// start out with alpha = 0
redLabel.alpha = 0.0f;
// add it to our parentView
[buttonSuperview addSubview:redLabel];
// height (determined by passed in value)
NSAssert(height >= 0, @"Height must be a positive number");
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:redLabel attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:height];
// width equal to button's width
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:redLabel attribute:NSLayoutAttributeWidth multiplier:1.0f constant:0.0f];
// center x == button's center x
NSLayoutConstraint *centerConstraint = [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:redLabel attribute:NSLayoutAttributeCenterX multiplier:1.0f constant:0.0f];
// now pin the top of the label to the bottom of the button
NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint constraintWithItem:redLabel attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:button attribute:NSLayoutAttributeBottom multiplier:1.0f constant:0.0f];
// add the height constraint to our label
[redLabel addConstraint:heightConstraint];
// and all the other constraints to our parent view
[buttonSuperview addConstraints:@[widthConstraint, centerConstraint, bottomConstraint]];
redLabel.alpha = 1.0f;
if (animate)
[UIView animateWithDuration:0.6f animations:^
[redLabel layoutIfNeeded];
];
self.previousRedLabel = redLabel;
@end
动画:
没有动画:
【讨论】:
假设您在自己的超级视图中拥有每个按钮。我会稍微编辑一下我的答案,以显示所有 3 个按钮是否都在同一个超级视图中以上是关于IOS / Objective-C:以编程方式在按钮下的中心线的主要内容,如果未能解决你的问题,请参考以下文章
使用 Objective-C 中的 Xcode 在 iOS 中以编程方式制作 UI 元素
在 Objective-C iOS 5 中为 Retina Display 以编程方式更改资源