使用
NSLayoutConstraint
类,可以创建基于视图基线 (NSLayoutAttributeBaseline
) 的约束。但是,我还没有看到任何文档描述 UIView
实际上如何为自动布局系统提供基线值。
如果我想创建一个定义基线的自定义
UIView
子类,我该怎么做? NSView
定义了一个 baselineOffsetFromBottom
方法,我认为它在 OS X 上以某种方式涉及,但是它在 iOS 中如何工作?
来自 UIView 文档:
viewForBaselineLayout
返回用于满足基线约束的视图。
- (UIView *)viewForBaselineLayout
返回值
约束系统应该用来满足基线约束的视图
我刚刚使用 Hopper 反汇编程序查看了 iOS 8.1 中的 UILabel,它实现了方法
_baselineOffsetFromBottom
和 _firstBaselineOffsetFromTop
,这两个方法依次由 -[UIView nsli_lowerAttribute:intoExpression:withCoefficient:forConstraint:]
调用,因此 UIKit 具有类似于 OS X 的私有方法,他们只是不暴露在公众面前。
_baselineOffsetFromBottom
和_firstBaselineOffsetFromTop
由UIView(返回0)、UILabel和UITextView实现。还有一个名为 _UIGlintyStringView
的类实现了 _baselineOffsetFromBottom
;没有其他 UIKit 类具有这些方法。
顺便说一句,当视图的基线发生变化时,它会执行以下操作:
__UITagLayoutConstraintsForConstantChangeForSelectedAttributes(self, __UILayoutAttributeIsBaselineAttribute)
好像没有什么特别的不能公开的;也许他们觉得没有必要或不想阻止人们写自己的标签。 UILabel 是一个相当复杂的类,涉及自定义 CoreAnimation 层 (_UILabelLayer) 和大量技巧,包括大量用于辅助功能支持的代码。
如果对齐视图不方便,您还可以覆盖基线值之一,即
firstBaselineOffsetFromTop
。例如,以下代码将第一个基线设置为视图的中间位置:
override var firstBaselineOffsetFromTop: CGFloat {
return bounds.midY
}
编辑:我的错,那是 MacOS。我相信 iOS 等效项是
firstBaselineAnchor
,它返回一个 NSLayoutYAxisAnchor
对象。我还没有测试过这个,但我希望这会产生类似的效果:
override var firstBaselineAnchor: NSLayoutYAxisAnchor {
return NSLayoutYAxisAnchor.anchorWithOffset(bounds.midY)
}
viewForBaselineLayout
已弃用,因此您必须使用
viewForFirstBaselineLayout
示例
- (UIView *)viewForFirstBaselineLayout {
// Use subLabel view for handling baseline layouts
return self.subLabel;
}
有两种方式使用私有API。
第一个是使用
-_baselineOffsetsAtSize:
。
- (CGPoint)_baselineOffsetsAtSize:(CGSize)size {
return CGPointMake(100., 100.);
}
另一种方法是将
-viewForFirstBaselineLayout
和 -viewForLastBaselineLayout
与 -_hasBaseline
一起使用。
- (BOOL)_hasBaseline {
return YES;
}
- (UIView *)viewForFirstBaselineLayout {
return self.orangeView;
}
- (UIView *)viewForLastBaselineLayout {
return self.orangeView;
}
完整代码:
@interface CustomBaselineView_1 : UIView
@property (retain, nonatomic, readonly) UIView *orangeView;
@end
@implementation CustomBaselineView_1
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.backgroundColor = UIColor.systemRedColor;
UIView *orangeView = [UIView new];
orangeView.backgroundColor = UIColor.systemOrangeColor;
orangeView.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:orangeView];
[NSLayoutConstraint activateConstraints:@[
[orangeView.topAnchor constraintEqualToAnchor:self.centerYAnchor],
[orangeView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor],
[orangeView.trailingAnchor constraintEqualToAnchor:self.trailingAnchor],
[orangeView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor]
]];
_orangeView = orangeView;
}
return self;
}
- (void)dealloc {
[_orangeView release];
[super dealloc];
}
//- (CGPoint)_baselineOffsetsAtSize:(CGSize)size {
// return CGPointMake(100., 100.);
//}
- (CGSize)intrinsicContentSize {
return self.superview.bounds.size;
}
- (BOOL)_hasBaseline {
return YES;
}
- (UIView *)viewForFirstBaselineLayout {
return self.orangeView;
}
- (UIView *)viewForLastBaselineLayout {
return self.orangeView;
}
@end
@interface CustomBaselineViewController ()
@property (retain, nonatomic, readonly) UIStackView *stackView;
@property (retain, nonatomic, readonly) CustomBaselineView_1 *customBaselineView_1;
@property (retain, nonatomic, readonly) UILabel *label_1;
@end
@implementation CustomBaselineViewController
@synthesize stackView = _stackView;
@synthesize customBaselineView_1 = _customBaselineView_1;
@synthesize label_1 = _label_1;
- (void)dealloc {
[_stackView release];
[_customBaselineView_1 release];
[super dealloc];
}
- (void)loadView {
self.view = self.stackView;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = UIColor.systemBackgroundColor;
}
- (UIStackView *)stackView {
if (auto stackView = _stackView) return stackView;
UIStackView *stackView = [[UIStackView alloc] initWithArrangedSubviews:@[
self.customBaselineView_1,
self.label_1
]];
stackView.axis = UILayoutConstraintAxisHorizontal;
stackView.distribution = UIStackViewDistributionFillEqually;
stackView.alignment = UIStackViewAlignmentFirstBaseline;
// stackView.alignment = UIStackViewAlignmentLastBaseline;
_stackView = [stackView retain];
return [stackView autorelease];
}
- (CustomBaselineView_1 *)customBaselineView_1 {
if (auto customBaselineView_1 = _customBaselineView_1) return customBaselineView_1;
CustomBaselineView_1 *customBaselineView_1 = [CustomBaselineView_1 new];
_customBaselineView_1 = [customBaselineView_1 retain];
return [customBaselineView_1 autorelease];
}
- (UILabel *)label_1 {
if (auto label_1 = _label_1) return label_1;
UILabel *label_1 = [UILabel new];
label_1.numberOfLines = 0;
label_1.backgroundColor = UIColor.systemCyanColor;
label_1.textColor = UIColor.systemOrangeColor;
label_1.font = [UIFont preferredFontForTextStyle:UIFontTextStyleLargeTitle];
label_1.text = @"HelloHelloHelloHelloHelloHelloHelloHelloHello";
_label_1 = [label_1 retain];
return [label_1 autorelease];
}
@end