如何创建定义基线的自定义 UIView?

问题描述 投票:0回答:5

使用

NSLayoutConstraint
类,可以创建基于视图基线 (
NSLayoutAttributeBaseline
) 的约束。但是,我还没有看到任何文档描述
UIView
实际上如何为自动布局系统提供基线值。

如果我想创建一个定义基线的自定义

UIView
子类,我该怎么做?
NSView
定义了一个
baselineOffsetFromBottom
方法,我认为它在 OS X 上以某种方式涉及,但是它在 iOS 中如何工作?

uiview autolayout
5个回答
11
投票

来自 UIView 文档:

viewForBaselineLayout

返回用于满足基线约束的视图。

- (UIView *)viewForBaselineLayout

返回值

约束系统应该用来满足基线约束的视图


10
投票

我刚刚使用 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) 和大量技巧,包括大量用于辅助功能支持的代码。


2
投票

如果对齐视图不方便,您还可以覆盖基线值之一,即

firstBaselineOffsetFromTop
。例如,以下代码将第一个基线设置为视图的中间位置:

override var firstBaselineOffsetFromTop: CGFloat {
    return bounds.midY
}

编辑:我的错,那是 MacOS。我相信 iOS 等效项是

firstBaselineAnchor
,它返回一个
NSLayoutYAxisAnchor
对象。我还没有测试过这个,但我希望这会产生类似的效果:

override var firstBaselineAnchor: NSLayoutYAxisAnchor {
    return NSLayoutYAxisAnchor.anchorWithOffset(bounds.midY)
}

2
投票
viewForBaselineLayout 

已弃用,因此您必须使用

viewForFirstBaselineLayout

示例

- (UIView *)viewForFirstBaselineLayout  {
    // Use subLabel view for handling baseline layouts
    return self.subLabel;
}

0
投票

有两种方式使用私有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
© www.soinside.com 2019 - 2024. All rights reserved.