博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
源码阅读:Masonry(六)—— MASViewConstraint
阅读量:6915 次
发布时间:2019-06-27

本文共 14588 字,大约阅读时间需要 48 分钟。

该文章阅读的 Masonry 的版本为 1.1.0。

这个类继承自 MASConstraint 类,是 MASCompositeConstraint 类的兄弟类,并且对 NSLayoutConstraint 类进行了封装。

1.公共属性

@property (nonatomic, strong, readonly) MASViewAttribute *firstViewAttribute;复制代码

这个属性是目标约束视图及其属性类对象,也就是 view1 + attr1


@property (nonatomic, strong, readonly) MASViewAttribute *secondViewAttribute;复制代码

这个属性是参考约束视图及其属性类对象,也就是 view2 + attr2

2.公共方法

- (id)initWithFirstViewAttribute:(MASViewAttribute *)firstViewAttribute;复制代码

以指定目标约束视图及其属性类对象初始化该类对象。


+ (NSArray *)installedConstraintsForView:(MAS_VIEW *)view;复制代码

返回指定视图作为 view1 上所有安装的约束,

3.私有分类 UIView+MASConstraints

.m 文件中,作者写了一个私有的 UIView 的分类,通过关联对象给这个分类添加了一个属性 mas_installedConstraints,用于保存安装到视图上的视图约束封装对象。

@interface MAS_VIEW (MASConstraints)@property (nonatomic, readonly) NSMutableSet *mas_installedConstraints;@end@implementation MAS_VIEW (MASConstraints)static char kInstalledConstraintsKey;- (NSMutableSet *)mas_installedConstraints {    NSMutableSet *constraints = objc_getAssociatedObject(self, &kInstalledConstraintsKey);    if (!constraints) {        constraints = [NSMutableSet set];        objc_setAssociatedObject(self, &kInstalledConstraintsKey, constraints, OBJC_ASSOCIATION_RETAIN_NONATOMIC);    }    return constraints;}@end复制代码

4.类扩展

/** 保存参考约束视图及其属性类对象,也就是 view2 + attr2。 */@property (nonatomic, strong, readwrite) MASViewAttribute *secondViewAttribute;复制代码
/** 保存安装约束的接收视图。 */@property (nonatomic, weak) MAS_VIEW *installedView;复制代码
/** 保存约束对象。 */@property (nonatomic, weak) MASLayoutConstraint *layoutConstraint;复制代码
/** 保存约束关系,也就是 relation。 */@property (nonatomic, assign) NSLayoutRelation layoutRelation;复制代码
/** 保存约束优先级。 */@property (nonatomic, assign) MASLayoutPriority layoutPriority;复制代码
/** 保存约束乘数,也就是 multiplier。 */@property (nonatomic, assign) CGFloat layoutMultiplier;复制代码
/** 保存约束常数,也就是 c。 */@property (nonatomic, assign) CGFloat layoutConstant;复制代码
/** 保存是否已经设置过约束关系。 */@property (nonatomic, assign) BOOL hasLayoutRelation;复制代码
/** 保存约束对象标识 */@property (nonatomic, strong) id mas_key;复制代码
/** 保存安装约束时是否使用动画 */@property (nonatomic, assign) BOOL useAnimator;复制代码

5.实现

5.1 公共方法

- (id)initWithFirstViewAttribute:(MASViewAttribute *)firstViewAttribute {    self = [super init];    if (!self) return nil;        // 保存参数    _firstViewAttribute = firstViewAttribute;    // 设置属性    self.layoutPriority = MASLayoutPriorityRequired;    self.layoutMultiplier = 1;        return self;}复制代码
+ (NSArray *)installedConstraintsForView:(MAS_VIEW *)view {    // 获取保存的所有元素    return [view.mas_installedConstraints allObjects];}复制代码
- (void)setSecondViewAttribute:(id)secondViewAttribute {    if ([secondViewAttribute isKindOfClass:NSValue.class]) {        // 如果传入的约束属性是 NSValue 类型的,就调用父类 MASConstraint 中的方法处理        [self setLayoutConstantWithValue:secondViewAttribute];    } else if ([secondViewAttribute isKindOfClass:MAS_VIEW.class]) {        // 如果传入的约束属性是 UIView 类型的,就以传入的视图和 attr1 实例化一个约束视图及其属性类并保存        _secondViewAttribute = [[MASViewAttribute alloc] initWithView:secondViewAttribute layoutAttribute:self.firstViewAttribute.layoutAttribute];    } else if ([secondViewAttribute isKindOfClass:MASViewAttribute.class]) {        // 如果传入的约束属性是 MASViewAttribute 类型的,就直接保存。        _secondViewAttribute = secondViewAttribute;    } else {        // 传入的参数,只能是上面三种情况之一        NSAssert(NO, @"attempting to add unsupported attribute: %@", secondViewAttribute);    }}复制代码

5.2 私有方法

/** 重写了类扩展中添加的属性 layoutConstant 的 setter */- (void)setLayoutConstant:(CGFloat)layoutConstant {    // 保存参数    _layoutConstant = layoutConstant;#if TARGET_OS_MAC && !(TARGET_OS_IPHONE || TARGET_OS_TV)    // 兼容其他系统    if (self.useAnimator) {        [self.layoutConstraint.animator setConstant:layoutConstant];    } else {        self.layoutConstraint.constant = layoutConstant;    }#else    // 直接将参数赋值给约束对象的 constant 属性    self.layoutConstraint.constant = layoutConstant;#endif}复制代码
/** 重写了类扩展中添加的属性 layoutRelation 的 setter */- (void)setLayoutRelation:(NSLayoutRelation)layoutRelation {    // 保存参数    _layoutRelation = layoutRelation;    // 保存状态    self.hasLayoutRelation = YES;}复制代码
/** 判断当前系统版本是否支持 isActive 属性,iOS8 之后的版本支持这个属性,用于版本兼容 */- (BOOL)supportsActiveProperty {    return [self.layoutConstraint respondsToSelector:@selector(isActive)];}复制代码
/** 判断约束对象是否处于活动状态 */- (BOOL)isActive {    BOOL active = YES;    // 判断是否支持这个属性    if ([self supportsActiveProperty]) {        // 获取该属性的值        active = [self.layoutConstraint isActive];    }    return active;}复制代码
/** 判断约束对象是否已安装 */- (BOOL)hasBeenInstalled {    // 必须有约束对象,并且约束对象处于活动状态,才是已安装    return (self.layoutConstraint != nil) && [self isActive];}复制代码
/** 获取与指定约束对象相似的约束对象 */- (MASLayoutConstraint *)layoutConstraintSimilarTo:(MASLayoutConstraint *)layoutConstraint {    // 遍历要设置约束视图的所有已设置的约束    // 除了约束对象的常数 c 之外,其他属性必须都相同,才是相似的约束对象    for (NSLayoutConstraint *existingConstraint in self.installedView.constraints.reverseObjectEnumerator) {        if (![existingConstraint isKindOfClass:MASLayoutConstraint.class]) continue;        if (existingConstraint.firstItem != layoutConstraint.firstItem) continue;        if (existingConstraint.secondItem != layoutConstraint.secondItem) continue;        if (existingConstraint.firstAttribute != layoutConstraint.firstAttribute) continue;        if (existingConstraint.secondAttribute != layoutConstraint.secondAttribute) continue;        if (existingConstraint.relation != layoutConstraint.relation) continue;        if (existingConstraint.multiplier != layoutConstraint.multiplier) continue;        if (existingConstraint.priority != layoutConstraint.priority) continue;        return (id)existingConstraint;    }    return nil;}复制代码

5.3 父类方法

- (MASConstraint * (^)(CGFloat))multipliedBy {    return ^id(CGFloat multiplier) {        // 已安装的约束无法修改        NSAssert(!self.hasBeenInstalled,                 @"Cannot modify constraint multiplier after it has been installed");        // 保存参数        self.layoutMultiplier = multiplier;        return self;    };}复制代码
- (MASConstraint * (^)(CGFloat))dividedBy {    return ^id(CGFloat divider) {        // 已安装的约束无法修改        NSAssert(!self.hasBeenInstalled,                 @"Cannot modify constraint multiplier after it has been installed");        // 保存参数的倒数        self.layoutMultiplier = 1.0/divider;        return self;    };}复制代码
- (MASConstraint * (^)(MASLayoutPriority))priority {    return ^id(MASLayoutPriority priority) {        // 已安装的约束无法修改        NSAssert(!self.hasBeenInstalled,                 @"Cannot modify constraint priority after it has been installed");        // 保存参数        self.layoutPriority = priority;        return self;    };}复制代码
- (MASConstraint *)with {    // 重写这个方法主要是为了返回当前类的类型,否则返回的对象还是其父类 MASConstraint 类型的对象    return self;}复制代码
- (MASConstraint *)and {    // 作用同上    return self;}复制代码
- (MASConstraint * (^)(id))key {    return ^id(id key) {        // 保存传入的参数        self.mas_key = key;        return self;    };}复制代码
- (void)setInsets:(MASEdgeInsets)insets {    // 获取已设置的 attr1    NSLayoutAttribute layoutAttribute = self.firstViewAttribute.layoutAttribute;    // 根据 attr1 设置不同的常数 c    switch (layoutAttribute) {        case NSLayoutAttributeLeft:        case NSLayoutAttributeLeading:            self.layoutConstant = insets.left;            break;        case NSLayoutAttributeTop:            self.layoutConstant = insets.top;            break;        case NSLayoutAttributeBottom:            self.layoutConstant = -insets.bottom;            break;        case NSLayoutAttributeRight:        case NSLayoutAttributeTrailing:            self.layoutConstant = -insets.right;            break;        default:            break;    }}复制代码
- (void)setInset:(CGFloat)inset {    // 调用上面的方法    [self setInsets:(MASEdgeInsets){.top = inset, .left = inset, .bottom = inset, .right = inset}];}复制代码
- (void)setOffset:(CGFloat)offset {    // 记录参数    self.layoutConstant = offset;}复制代码
- (void)setSizeOffset:(CGSize)sizeOffset {    // 获取已设置的 attr1    NSLayoutAttribute layoutAttribute = self.firstViewAttribute.layoutAttribute;    // 根据 attr1 设置不同的常数 c    switch (layoutAttribute) {        case NSLayoutAttributeWidth:            self.layoutConstant = sizeOffset.width;            break;        case NSLayoutAttributeHeight:            self.layoutConstant = sizeOffset.height;            break;        default:            break;    }}复制代码
- (void)setCenterOffset:(CGPoint)centerOffset {    // 获取已设置的 attr1    NSLayoutAttribute layoutAttribute = self.firstViewAttribute.layoutAttribute;    // 根据 attr1 设置不同的常数 c    switch (layoutAttribute) {        case NSLayoutAttributeCenterX:            self.layoutConstant = centerOffset.x;            break;        case NSLayoutAttributeCenterY:            self.layoutConstant = centerOffset.y;            break;        default:            break;    }}复制代码
- (void)activate {    // 直接调用该类的 install 方法    [self install];}复制代码
- (void)deactivate {    // 直接调用该类的 uninstall 方法    [self uninstall];}复制代码
- (void)install {    // 如果已经安装了就直接返回    if (self.hasBeenInstalled) {        return;    }        // 兼容 iOS8 以后的版本    if ([self supportsActiveProperty] && self.layoutConstraint) {        // 将约束对象的活动状态激活        self.layoutConstraint.active = YES;        // 保存已安装的视图约束封装对象        [self.firstViewAttribute.view.mas_installedConstraints addObject:self];        // 返回        return;    }        // 兼容 iOS8 之前的版本    // 获取生成约束对象的参数    MAS_VIEW *firstLayoutItem = self.firstViewAttribute.item;    NSLayoutAttribute firstLayoutAttribute = self.firstViewAttribute.layoutAttribute;    MAS_VIEW *secondLayoutItem = self.secondViewAttribute.item;    NSLayoutAttribute secondLayoutAttribute = self.secondViewAttribute.layoutAttribute;    // 如果设置了像 make.left.equalTo(@10) 这样的约束    if (!self.firstViewAttribute.isSizeAttribute && !self.secondViewAttribute) {        secondLayoutItem = self.firstViewAttribute.view.superview;        secondLayoutAttribute = firstLayoutAttribute;    }        // 实例化约束对象    MASLayoutConstraint *layoutConstraint        = [MASLayoutConstraint constraintWithItem:firstLayoutItem                                        attribute:firstLayoutAttribute                                        relatedBy:self.layoutRelation                                           toItem:secondLayoutItem                                        attribute:secondLayoutAttribute                                       multiplier:self.layoutMultiplier                                         constant:self.layoutConstant];    // 设置约束对象的属性    layoutConstraint.priority = self.layoutPriority;    layoutConstraint.mas_key = self.mas_key;        if (self.secondViewAttribute.view) {        // 如果设置了 view2        // 获取 view1 和 view2 的公共父视图        MAS_VIEW *closestCommonSuperview = [self.firstViewAttribute.view mas_closestCommonSuperview:self.secondViewAttribute.view];        // view1 和 view 必须有公共父视图        NSAssert(closestCommonSuperview,                 @"couldn't find a common superview for %@ and %@",                 self.firstViewAttribute.view, self.secondViewAttribute.view);        // 要设置约束的视图就是他们的公共父视图        self.installedView = closestCommonSuperview;    } else if (self.firstViewAttribute.isSizeAttribute) {        // 如果设置的属性为 size 类型的, 要设置约束的视图就是 view1        self.installedView = self.firstViewAttribute.view;    } else {        // 否则,要设置约束的视图就是 view1 的父视图        self.installedView = self.firstViewAttribute.view.superview;    }    // 创建变量保存之前添加的约束    MASLayoutConstraint *existingConstraint = nil;    if (self.updateExisting) {        // 如果需要更新约束,就获取之前的约束对象        existingConstraint = [self layoutConstraintSimilarTo:layoutConstraint];    }    if (existingConstraint) {        // 如果之前安装过约束对象        // 更新约束对象的常数 c        existingConstraint.constant = layoutConstraint.constant;        // 保存新的约束对象        self.layoutConstraint = existingConstraint;    } else {        // 如果之前没有安装过约束对象        // 安装约束对象        [self.installedView addConstraint:layoutConstraint];        // 保存安装的约束对象        self.layoutConstraint = layoutConstraint;        [firstLayoutItem.mas_installedConstraints addObject:self];    }}复制代码
- (void)uninstall {    // 兼容 iOS8 以后的版本    if ([self supportsActiveProperty]) {        // 将约束对象的活动状态关闭        self.layoutConstraint.active = NO;        // 从集合中移除        [self.firstViewAttribute.view.mas_installedConstraints removeObject:self];        return;    }        // 兼容 iOS8 之前的版本    // 从视图上移除约束    [self.installedView removeConstraint:self.layoutConstraint];    // 将属性置空    self.layoutConstraint = nil;    self.installedView = nil;        // 从集合中移除    [self.firstViewAttribute.view.mas_installedConstraints removeObject:self];}复制代码

5.4 私有分类方法

- (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation {    return ^id(id attribute, NSLayoutRelation relation) {        if ([attribute isKindOfClass:NSArray.class]) {            // 如果是传入的属性是 NSArray 类型的            // 已设置约束关系的约束无法修改            NSAssert(!self.hasLayoutRelation, @"Redefinition of constraint relation");            // 创建属性保存约束视图及其属性类            NSMutableArray *children = NSMutableArray.new;            // 遍历传入的参数            for (id attr in attribute) {                // 获取当前对象的副本,设置属性,并添加到数组                MASViewConstraint *viewConstraint = [self copy];                viewConstraint.layoutRelation = relation;                viewConstraint.secondViewAttribute = attr;                [children addObject:viewConstraint];            }            // 创建多约束封装对象            MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children];            // 设置代理对象为当前对象的代理对象,也就是 MASConstraintMaker 对象            compositeConstraint.delegate = self.delegate;            // 调用代理对象实现的方法进行约束对象替换            [self.delegate constraint:self shouldBeReplacedWithConstraint:compositeConstraint];            // 返回多约束封装对象            return compositeConstraint;        } else {            // 要么没有设置过约束关系            // 要么设置过约束关系,但是新设置的约束关系和之前设置的是相同的,并且传入的参数是 NSValue 类型的            NSAssert(!self.hasLayoutRelation || self.layoutRelation == relation && [attribute isKindOfClass:NSValue.class], @"Redefinition of constraint relation");            // 保存参数            self.layoutRelation = relation;            self.secondViewAttribute = attribute;            return self;        }    };}复制代码
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {    // 约束属性应该在添加约束关系之前添加    NSAssert(!self.hasLayoutRelation, @"Attributes should be chained before defining the constraint relation");    // 调用代理对象实现的方法进行约束对象添加    return [self.delegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute];}复制代码

6. 总结

该类封装了约束对象 NSLayoutConstraint ,具体实现了父类 MASConstraint 指定的功能。可以说是约束对象的管理类。

转载地址:http://boicl.baihongyu.com/

你可能感兴趣的文章
Vue cli 资源文件的引用
查看>>
OCP知识点讲解 之 Undo回滚基础知识
查看>>
跨碧岭观赏龙潭瀑布群
查看>>
Windows 命令打开无线WIFI热点
查看>>
编写一个算法,若M*N矩阵中某个元素为0,则将其所在的行与列清零。
查看>>
写一个算法计算n的阶乘末尾0的个数
查看>>
投资“杠杆”,你了解吗?
查看>>
知其所以然之永不遗忘的算法
查看>>
Java正则表达式语法大全
查看>>
apache设置自定义header
查看>>
点乘和叉乘在3D中的实际用法
查看>>
WPS田字格的做法
查看>>
Linux账号登录安全
查看>>
Linux 基础命令 – watch
查看>>
我的友情链接
查看>>
snavigator安装步骤
查看>>
python抓取jenkins slave信息写道mysql并展现到grafana
查看>>
debian 常用的源
查看>>
博为峰Java技术题-JavaSE 之标识符、注释
查看>>
陈松松:如何保证每天录制一个视频,一年365个原创视频
查看>>