| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710 |
- //
- // UIView+RAConstraint.m
- // Constraint
- //
- // Created by Jack on 2018/12/10.
- // Copyright © 2018年 Jack Template. All rights reserved.
- //
- #import "UIView+RAConstraint.h"
- #import <objc/runtime.h>
- #pragma mark - Constraint
- @interface RAConstraint ()
- @property (nonatomic,weak) id appliedView;
- @property (nonatomic,weak) NSLayoutConstraint *constraint;
- @property (nonatomic,assign) UILayoutPriority priority;
- @property (nonatomic,assign) BOOL validate;
- @property (nonatomic,copy) NSString *identifier;
- @property (nonatomic,assign) NSLayoutAttribute attribute;
- @property (nonatomic,assign) NSLayoutRelation relation;
- @property (nonatomic,weak) UIView *toView;
- @property (nonatomic,assign) NSLayoutAttribute toAttribute;
- @property (nonatomic,assign) CGFloat constant;
- @property (nonatomic,copy) RAConstraint *(^ra_p_offset)(CGFloat offset);
- @property (nonatomic,copy) RAConstraint *(^ra_p_lessThanOrEqualTo)(RAConstraint *constraint);
- @property (nonatomic,copy) RAConstraint *(^ra_p_equalTo)(RAConstraint *constraint);
- @property (nonatomic,copy) RAConstraint *(^ra_p_greaterThanOrEqualTo)(RAConstraint *constraint);
- @property (nonatomic,copy) RAConstraint *(^ra_p_priority)(UILayoutPriority prioruty);
- @property (nonatomic,strong) NSMutableDictionary<NSString *,RAConstraint *> *appendConstraints;
- @property (nonatomic,copy) RAConstraint *(^ra_p_append)(NSString *identifier);
- @property (nonatomic,copy) RAConstraint *(^ra_p_size_greaterThanOrEqualTo)(CGFloat size);
- @property (nonatomic,copy) RAConstraint *(^ra_p_size_lessThanOrEqualTo)(CGFloat size);
- @property (nonatomic,copy) void (^ra_p_modify)(CGFloat constant);
- @end
- @implementation RAConstraint
- - (instancetype)init {
- if (self = [super init]) {
-
- _attribute = NSLayoutAttributeNotAnAttribute;
- _relation = NSLayoutRelationEqual;
- _toAttribute = NSLayoutAttributeNotAnAttribute;
- _priority = UILayoutPriorityDefaultHigh;
-
- __weak typeof(self) weakSelf = self;
- self.ra_p_offset = [^RAConstraint *(CGFloat offset) {
-
- weakSelf.validate = YES;
- weakSelf.constant = offset;
- return weakSelf;
- } copy];
-
- self.ra_p_lessThanOrEqualTo = [^RAConstraint *(RAConstraint *constraint) {
-
- weakSelf.validate = YES;
- weakSelf.toView = constraint.appliedView;
- weakSelf.relation = NSLayoutRelationLessThanOrEqual;
- weakSelf.toAttribute = constraint.attribute;
-
- return weakSelf;
- } copy];
-
- self.ra_p_equalTo = [^RAConstraint *(RAConstraint *constraint) {
-
- weakSelf.validate = YES;
- weakSelf.toView = constraint.appliedView;
- weakSelf.relation = NSLayoutRelationEqual;
- weakSelf.toAttribute = constraint.attribute;
-
- return weakSelf;
- } copy];
-
- self.ra_p_greaterThanOrEqualTo = [^RAConstraint *(RAConstraint *constraint) {
-
- weakSelf.validate = YES;
- weakSelf.toView = constraint.appliedView;
- weakSelf.relation = NSLayoutRelationGreaterThanOrEqual;
- weakSelf.toAttribute = constraint.attribute;
-
- return weakSelf;
- } copy];
-
- self.ra_p_priority = [^RAConstraint *(UILayoutPriority priority) {
-
- weakSelf.priority = priority;
-
- return weakSelf;
- } copy];
-
- self.ra_p_append = [^RAConstraint *(NSString *identifier) {
-
- if (!identifier) {
- return nil;
- }
-
- if (weakSelf.appendConstraints[identifier]) {
- return weakSelf.appendConstraints[identifier];
- }
-
- RAConstraint *constraint = [RAConstraint new];
- constraint.appliedView = weakSelf.appliedView;
- constraint.attribute = weakSelf.attribute;
-
- weakSelf.appendConstraints[identifier] = constraint;
-
- return constraint;
-
- } copy];
-
- self.ra_p_size_lessThanOrEqualTo = [^RAConstraint *(CGFloat size) {
-
- if (weakSelf.attribute != NSLayoutAttributeWidth && weakSelf.attribute != NSLayoutAttributeHeight) {
- return weakSelf;
- }
-
- weakSelf.validate = YES;
- weakSelf.toView = nil;
- weakSelf.relation = NSLayoutRelationLessThanOrEqual;
- weakSelf.toAttribute = NSLayoutAttributeNotAnAttribute;
- weakSelf.ra_offset(size);
-
- return weakSelf;
- } copy];
-
- self.ra_p_size_greaterThanOrEqualTo = [^RAConstraint *(CGFloat size) {
-
- if (weakSelf.attribute != NSLayoutAttributeWidth && weakSelf.attribute != NSLayoutAttributeHeight) {
- return weakSelf;
- }
-
- weakSelf.validate = YES;
- weakSelf.toView = nil;
- weakSelf.relation = NSLayoutRelationGreaterThanOrEqual;
- weakSelf.toAttribute = NSLayoutAttributeNotAnAttribute;
- weakSelf.ra_offset(size);
-
- return weakSelf;
- } copy];
-
- self.ra_p_modify = [^(CGFloat constant) {
-
- weakSelf.constant = constant;
- if (weakSelf.constraint) {
- weakSelf.constraint.constant = constant;
- if ([weakSelf appliedViewIsView]) {
- UIView *appliedView = ((UIView *)weakSelf.appliedView);
- [appliedView.superview layoutIfNeeded];
- }
- }
-
- } copy];
- }
- return self;
- }
- - (RAConstraint *(^)(CGFloat))ra_offset {
- return self.ra_p_offset;
- }
- - (RAConstraint *(^)(RAConstraint *))ra_lessThanOrEqualTo {
- return self.ra_p_lessThanOrEqualTo;
- }
- - (RAConstraint *(^)(RAConstraint *))ra_equalTo {
- return self.ra_p_equalTo;
- }
- - (RAConstraint *(^)(RAConstraint *))ra_greaterThanOrEqualTo {
- return self.ra_p_greaterThanOrEqualTo;
- }
- - (RAConstraint *(^)(UILayoutPriority))ra_priority {
- return self.ra_p_priority;
- }
- - (RAConstraint *(^)(NSString *))ra_append {
- return self.ra_p_append;
- }
- - (RAConstraint *(^)(CGFloat))ra_size_lessThanOrEqualTo {
- return self.ra_p_size_lessThanOrEqualTo;
- }
- - (RAConstraint *(^)(CGFloat))ra_size_greaterThanOrEqualTo {
- return self.ra_p_size_greaterThanOrEqualTo;
- }
- - (void (^)(CGFloat))ra_modify {
- return self.ra_p_modify;
- }
- - (void)ra_uninstall {
-
- if (self.appliedView && self.appliedViewIsView && self.validate && self.constraint) {
-
- UIView *appliedView = ((UIView *)self.appliedView);
- if ((self.attribute == NSLayoutAttributeWidth || self.attribute == NSLayoutAttributeHeight) && !self.toView) {
-
- [appliedView removeConstraint:self.constraint];
- } else {
-
- [appliedView.superview removeConstraint:self.constraint];
- }
- }
-
- [self.appendConstraints enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, RAConstraint * _Nonnull obj, BOOL * _Nonnull stop) {
-
- [obj ra_uninstall];
- }];
-
- self.validate = NO;
- self.constraint = nil;
- self.identifier = nil;
-
- self.toView = nil;
- self.relation = NSLayoutRelationEqual;
- self.toAttribute = NSLayoutAttributeNotAnAttribute;
- self.constant = 0;
- self.priority = UILayoutPriorityDefaultHigh;
-
- [self.appendConstraints removeAllObjects];
- }
- - (void)ra_install {
-
- if (self.appliedView && self.appliedViewIsView && self.validate && !self.constraint) {
-
- UIView *appliedView = ((UIView *)self.appliedView);
- NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:appliedView
- attribute:self.attribute
- relatedBy:self.relation
- toItem:self.toView
- attribute:self.toAttribute
- multiplier:1
- constant:self.constant];
- self.constraint = constraint;
- self.constraint.priority = self.priority;
- self.constraint.identifier = self.identifier;
-
- if ((self.attribute == NSLayoutAttributeWidth || self.attribute == NSLayoutAttributeHeight) && !self.toView) {
- [appliedView addConstraint:self.constraint];
- } else{
- [appliedView.superview addConstraint:self.constraint];
- }
- }
-
- [self.appendConstraints enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, RAConstraint * _Nonnull obj, BOOL * _Nonnull stop) {
-
- [obj ra_install];
- }];
- }
- - (NSMutableDictionary *)appendConstraints {
- if (!_appendConstraints) {
- _appendConstraints = [NSMutableDictionary dictionary];
- }
- return _appendConstraints;
- }
- - (BOOL)appliedViewIsView {
- BOOL isView = [self.appliedView isKindOfClass:[UIView class]];
- return isView;
- }
- @end
- #pragma mark - Maker
- @interface RAConstraintMaker ()
- @property (nonatomic,weak) UIView *appliedView;
- @property (nonatomic,strong) RAConstraint *left;
- @property (nonatomic,strong) RAConstraint *top;
- @property (nonatomic,strong) RAConstraint *right;
- @property (nonatomic,strong) RAConstraint *bottom;
- @property (nonatomic,strong) RAConstraint *centerX;
- @property (nonatomic,strong) RAConstraint *centerY;
- @property (nonatomic,strong) RAConstraint *baseLine;
- @property (nonatomic,strong) RAConstraint *width;
- @property (nonatomic,strong) RAConstraint *height;
- @property (nonatomic,strong) RAConstraint *ra_safeAreaLeft API_AVAILABLE(ios(11.0),tvos(11.0));
- @property (nonatomic,strong) RAConstraint *ra_safeAreaTop API_AVAILABLE(ios(11.0),tvos(11.0));
- @property (nonatomic,strong) RAConstraint *ra_safeAreaRight API_AVAILABLE(ios(11.0),tvos(11.0));
- @property (nonatomic,strong) RAConstraint *ra_safeAreaBottom API_AVAILABLE(ios(11.0),tvos(11.0));
- @property (nonatomic,strong) NSMutableArray<RAConstraint *> *constraints;
- @property (nonatomic,copy) void (^ra_modifyConstraint)(NSString *identifier, CGFloat constant);
- @end
- static const UILayoutPriority kRALayoutPriorityNone = -1;
- @implementation RAConstraintMaker
- - (instancetype)initWithAppliedView:(UIView *)view {
- if (self = [super init]) {
- self.appliedView = view;
- [self.constraints addObjectsFromArray:@[
- self.left,
- self.top,
- self.right,
- self.bottom,
- self.centerX,
- self.centerY,
- self.baseLine,
- self.width,
- self.height
- ]];
-
- self.ra_horizontalHuggingPriority = kRALayoutPriorityNone;
- self.ra_verticalHuggingPriority = kRALayoutPriorityNone;
-
- self.ra_horizontalCompressionResistancePriority = kRALayoutPriorityNone;
- self.ra_verticalCompressionResistancePriority = kRALayoutPriorityNone;
-
- __weak typeof(self) weakSelf = self;
- self.ra_modifyConstraint = [^(NSString *identifier, CGFloat constant) {
-
- NSDictionary *constraintRes = [weakSelf findConstraintOfView:weakSelf.appliedView byIdentifier:identifier];
- if (constraintRes) {
-
- UIView *view = constraintRes[@"view"];
- NSLayoutConstraint *constraint = constraintRes[@"constraint"];
-
- constraint.constant = constant;
- [view layoutIfNeeded];
- }
-
- } copy];
- }
- return self;
- }
- - (NSDictionary *)findConstraintOfView:(UIView *)view byIdentifier:(NSString *)identifier {
- if (!view || !identifier) {
- return nil;
- }
- NSLayoutConstraint *result = nil;
-
- NSArray<NSLayoutConstraint *> *constraintArray = view.constraints;
- for (NSLayoutConstraint *constraint in constraintArray) {
- if (constraint.identifier && [constraint.identifier isEqualToString:identifier]) {
- result = constraint;
- break;
- }
- }
- if (result) {
- return @{
- @"view" : view,
- @"constraint" : result
- };
- } else {
- if (view.superview) {
- return [self findConstraintOfView:view.superview byIdentifier:identifier];
- } else {
- return nil;
- }
- }
- }
- - (NSMutableArray<RAConstraint *> *)constraints {
- if (!_constraints) {
- _constraints = [NSMutableArray array];
- }
- return _constraints;
- }
- - (RAConstraint *)left {
- if (!_left) {
- _left = [RAConstraint new];
- _left.attribute = NSLayoutAttributeLeft;
- _left.appliedView = self.appliedView;
- }
- return _left;
- }
- - (RAConstraint *)top {
- if (!_top) {
- _top = [RAConstraint new];
- _top.attribute = NSLayoutAttributeTop;
- _top.appliedView = self.appliedView;
- }
- return _top;
- }
- - (RAConstraint *)right {
- if (!_right) {
- _right = [RAConstraint new];
- _right.attribute = NSLayoutAttributeRight;
- _right.appliedView = self.appliedView;
- }
- return _right;
- }
- - (RAConstraint *)bottom {
- if (!_bottom) {
- _bottom = [RAConstraint new];
- _bottom.attribute = NSLayoutAttributeBottom;
- _bottom.appliedView = self.appliedView;
- }
- return _bottom;
- }
- - (RAConstraint *)centerX {
- if (!_centerX) {
- _centerX = [RAConstraint new];
- _centerX.attribute = NSLayoutAttributeCenterX;
- _centerX.appliedView = self.appliedView;
- }
- return _centerX;
- }
- - (RAConstraint *)centerY {
- if (!_centerY) {
- _centerY = [RAConstraint new];
- _centerY.attribute = NSLayoutAttributeCenterY;
- _centerY.appliedView = self.appliedView;
- }
- return _centerY;
- }
- - (RAConstraint *)baseLine {
- if (!_baseLine) {
- _baseLine = [RAConstraint new];
- _baseLine.attribute = NSLayoutAttributeBaseline;
- _baseLine.appliedView = self.appliedView;
- }
- return _baseLine;
- }
- - (RAConstraint *)width {
- if (!_width) {
- _width = [RAConstraint new];
- _width.attribute = NSLayoutAttributeWidth;
- _width.appliedView = self.appliedView;
- }
- return _width;
- }
- - (RAConstraint *)height {
- if (!_height) {
- _height = [RAConstraint new];
- _height.attribute = NSLayoutAttributeHeight;
- _height.appliedView = self.appliedView;
- }
- return _height;
- }
- // safe area
- - (RAConstraint *)ra_safeAreaLeft {
- if (!_ra_safeAreaLeft) {
- _ra_safeAreaLeft = [RAConstraint new];
- _ra_safeAreaLeft.attribute = NSLayoutAttributeLeft;
- _ra_safeAreaLeft.appliedView = self.appliedView.safeAreaLayoutGuide;
- }
- return _ra_safeAreaLeft;
- }
- - (RAConstraint *)ra_safeAreaTop {
- if (!_ra_safeAreaTop) {
- _ra_safeAreaTop = [RAConstraint new];
- _ra_safeAreaTop.attribute = NSLayoutAttributeTop;
- _ra_safeAreaTop.appliedView = self.appliedView.safeAreaLayoutGuide;
- }
- return _ra_safeAreaTop;
- }
- - (RAConstraint *)ra_safeAreaRight {
- if (!_ra_safeAreaRight) {
- _ra_safeAreaRight = [RAConstraint new];
- _ra_safeAreaRight.attribute = NSLayoutAttributeRight;
- _ra_safeAreaRight.appliedView = self.appliedView.safeAreaLayoutGuide;
- }
- return _ra_safeAreaRight;
- }
- - (RAConstraint *)ra_safeAreaBottom {
- if (!_ra_safeAreaBottom) {
- _ra_safeAreaBottom = [RAConstraint new];
- _ra_safeAreaBottom.attribute = NSLayoutAttributeBottom;
- _ra_safeAreaBottom.appliedView = self.appliedView.safeAreaLayoutGuide;
- }
- return _ra_safeAreaBottom;
- }
- - (void)ra_install {
- if (self.appliedView) {
-
- // 抗拉伸
- if (self.ra_horizontalHuggingPriority != kRALayoutPriorityNone) {
- [self.appliedView setContentHuggingPriority:self.ra_horizontalHuggingPriority forAxis:UILayoutConstraintAxisHorizontal];
- }
-
- if (self.ra_verticalHuggingPriority != kRALayoutPriorityNone) {
- [self.appliedView setContentHuggingPriority:self.ra_verticalHuggingPriority forAxis:UILayoutConstraintAxisVertical];
- }
-
- // 抗压缩
- if (self.ra_horizontalCompressionResistancePriority != kRALayoutPriorityNone) {
- [self.appliedView setContentCompressionResistancePriority:self.ra_horizontalCompressionResistancePriority forAxis:UILayoutConstraintAxisHorizontal];
- }
-
- if (self.ra_verticalCompressionResistancePriority != kRALayoutPriorityNone) {
- [self.appliedView setContentCompressionResistancePriority:self.ra_verticalCompressionResistancePriority forAxis:UILayoutConstraintAxisVertical];
- }
-
- // normal constraint
- for (RAConstraint *constraint in self.constraints) {
- [constraint ra_install];
- }
- }
- }
- - (void)ra_uninstall {
-
- if (self.appliedView) {
-
- for (RAConstraint *constraint in self.constraints) {
- [constraint ra_uninstall];
- }
- }
- }
- @end
- #pragma mark - UIView
- static const char RAMaker = '\a';
- @implementation UIView (RAConstraint)
- - (void)setRA_Maker:(RAConstraintMaker *)maker {
-
- if (maker) {
- objc_setAssociatedObject(self, &RAMaker, maker, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
- }
- - (RAConstraintMaker *)ra_maker {
- RAConstraintMaker *maker = objc_getAssociatedObject(self, &RAMaker);
- if (maker == nil) {
- maker = [[RAConstraintMaker alloc] initWithAppliedView:self];
- [self setRA_Maker:maker];
- }
- return maker;
- }
- - (void)ra_applyConstraints:(void(^)(RAConstraintMaker *maker))apply {
- if (self.superview) {
- // self.superview.translatesAutoresizingMaskIntoConstraints = NO;
- self.translatesAutoresizingMaskIntoConstraints = NO;
-
- RAConstraintMaker *maker = [self ra_maker];
- if (apply) {
- apply(maker);
- }
-
- [self applyMaker:maker];
- }
- }
- - (void)applyMaker:(RAConstraintMaker *)maker {
-
- if (maker) {
- [self setRA_Maker:maker];
- [maker ra_install];
-
- [self.superview layoutIfNeeded];
- }
- }
- - (void)ra_remakeConstraints:(void(^)(RAConstraintMaker *maker))remake {
-
- [[self ra_maker] ra_uninstall];
-
- [self ra_applyConstraints:remake];
- }
- #pragma mark - P
- - (RAConstraint *)left {
- return [self ra_maker].left;
- }
- - (RAConstraint *)top {
- return [self ra_maker].top;
- }
- - (RAConstraint *)right {
- return [self ra_maker].right;
- }
- - (RAConstraint *)bottom {
- return [self ra_maker].bottom;
- }
- - (RAConstraint *)centerX {
- return [self ra_maker].centerX;
- }
- - (RAConstraint *)centerY {
- return [self ra_maker].centerY;
- }
- - (RAConstraint *)baseLine {
- return [self ra_maker].baseLine;
- }
- - (RAConstraint *)width {
- return [self ra_maker].width;
- }
- - (RAConstraint *)height {
- return [self ra_maker].height;
- }
- // safe area
- - (RAConstraint *)ra_safeAreaLeft {
- return [self ra_maker].ra_safeAreaLeft;
- }
- - (RAConstraint *)ra_safeAreaTop {
- return [self ra_maker].ra_safeAreaTop;
- }
- - (RAConstraint *)ra_safeAreaRight {
- return [self ra_maker].ra_safeAreaRight;
- }
- - (RAConstraint *)ra_safeAreaBottom {
- return [self ra_maker].ra_safeAreaBottom;
- }
- // modify
- - (void (^)(NSString *, CGFloat))ra_modifyConstraint {
- return [self ra_maker].ra_modifyConstraint;
- }
- @end
- #pragma mark - View Controller
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Wdeprecated-declarations"
- @implementation UIViewController (RAConstraint)
- - (RAConstraint *)ra_topLayoutGuid {
-
- RAConstraint *constraint = [RAConstraint new];
- constraint.attribute = NSLayoutAttributeBottom;
- constraint.appliedView = self.topLayoutGuide;
- return constraint;
- }
- - (RAConstraint *)ra_topLayoutGuidTop {
-
- RAConstraint *constraint = [RAConstraint new];
- constraint.attribute = NSLayoutAttributeTop;
- constraint.appliedView = self.topLayoutGuide;
- return constraint;
- }
- - (RAConstraint *)ra_topLayoutGuidBottom {
-
- RAConstraint *constraint = [RAConstraint new];
- constraint.attribute = NSLayoutAttributeBottom;
- constraint.appliedView = self.topLayoutGuide;
- return constraint;
- }
- - (RAConstraint *)ra_bottomLayoutGuid {
-
- RAConstraint *constraint = [RAConstraint new];
- constraint.attribute = NSLayoutAttributeTop;
- constraint.appliedView = self.bottomLayoutGuide;
- return constraint;
- }
- - (RAConstraint *)ra_bottomLayoutGuidTop {
- RAConstraint *constraint = [RAConstraint new];
- constraint.attribute = NSLayoutAttributeTop;
- constraint.appliedView = self.bottomLayoutGuide;
- return constraint;
- }
- - (RAConstraint *)ra_bottomLayoutGuidBottom {
- RAConstraint *constraint = [RAConstraint new];
- constraint.attribute = NSLayoutAttributeBottom;
- constraint.appliedView = self.bottomLayoutGuide;
- return constraint;
- }
- #pragma clang diagnostic pop
- @end
|