MasonryFloatLayout: Floating layout based on Masonry


Preface


Using Masonry for layout in iOS is a daily operation, but when it is similar to the floating layout of a web page, the specific diagram is as follows.

Remove one of the elements, and the remaining elements will move in a certain direction.On the Web side, this layout method is called a floating layout.

In addition, there is the following situation.Although it is also floating, the constraint relationship is still maintained on the other side.This is also a relatively common constraint situation in iOS.

Using Masonry in iOS to implement the above process is actually very troublesome.If we are based on Masonry, based on this situation, we generally have two ways of writing, one is the state exhaustive method, and the other is the temporary view variable recording method.

State exhaustive legislation:

The state exhaustive method is to list all the view combinations and add the corresponding constraint layout. For specific examples, you can see the following code.

- (void)loseConstraintsAction {
    if (!_A.hidden && !_B.hidden && !_C.hidden) {
        [_A mas_remakeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo(self.view).offset(20.0f);
        }];
        [_B mas_remakeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo(self.A.mas_right).offset(8.0f);
        }];
        [_C mas_remakeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo(self.B.mas_right).offset(8.0f);
        }];
    }
    
    if (!_A.hidden && _B.hidden && !_C.hidden) {
        [_A mas_remakeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo(self.view).offset(20.0f);
        }];
        [_C mas_remakeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo(self.A.mas_right).offset(8.0f);
        }];
    }
    if (_A.hidden && _B.hidden && !_C.hidden) {
        [_C mas_remakeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo(self.view).offset(8.0f);
        }];
    }
}

The state exhaustion method can be regarded as a very violent solution. The state exhaustion method is written in various forms, but the overall idea is the same. Although the exhaustion method is simple to understand, the disadvantages are obvious, and the amount of code cannot be underestimated , Every time we add a combination, we need one more constraint scheme...


Temporary view variable recording method:

The temporary variable recording method is to use a temporary variable record to establish constraints with which view. The specific code example is shown below.

- (void)loseConstraintsAction {
    UIView *lastView = self.view;
    if (!_A.hidden) {
        [_A mas_remakeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo([lastView isEqual:self.view] ? lastView : lastView.mas_right).offset(20.0f);
        }];
        lastView = _A;
    }
    if (!_B.hidden) {
        [_B mas_remakeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo([lastView isEqual:self.view] ? lastView : lastView.mas_right).offset(8.0f);
        }];
        lastView = _B;
    }
    if (!_C.hidden) {
        [_C mas_remakeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo([lastView isEqual:self.view] ? lastView : lastView.mas_right).offset(8.0f);
        }];
    }
}

Compared with the exhaustive method, the code structure has been greatly improved, but the overall code still makes people feel less satisfied.

From this, Sao Dong wondered whether it was possible to encapsulate a library based on Masonry to achieve this floating effect? ​​So I encapsulated a Masonry-based floating layout MasonryFloatLayout .

Before talking about the implementation, let's take a look at how to use the packaged MasonryFloatLayout .


MasonryFloatLayout use


  • First import the MasonryFloatLayout folder in Demo , as shown in the figure below.

  • What needs to be changed here is the file path address of Masonry of UIViewFloatLayoutHeader . If an error is reported, please import the correct file path address.
#ifndef UIViewFloatLayoutHeader_h
#define UIViewFloatLayoutHeader_h

#import "Masonry/Masonry.h"

#endif /* UIViewFloatLayoutHeader_h */
  • Introduce the header file in the required ViewController or View.
#import "NSArray+FloatLayout.h"
  • Suppose we now have three Views and need to perform floating layout, then we must first complete the initialization work. Including the declaration of View properties and the implementation of lazy loading View methods, of course, you can follow your habits.
@property (nonatomic, strong) UIView *firstView;
@property (nonatomic, strong) UIView *secondView;
@property (nonatomic, strong) UILabel *thirdView;
- (UIView *)firstView {
    if (_firstView == nil) {
        _firstView = [[UIView alloc] initWithFrame:CGRectZero];
        _firstView.backgroundColor = [UIColor redColor];
    }
    return _firstView;
}

- (UIView *)secondView {
    if (_secondView == nil) {
        _secondView = [[UIView alloc] initWithFrame:CGRectZero];
        _secondView.backgroundColor = [UIColor orangeColor];
    }
    return _secondView;
}

- (UILabel *)thirdView {
    if (_thirdView == nil) {
        _thirdView = [[UILabel alloc] initWithFrame:CGRectZero];
        _thirdView.backgroundColor = [UIColor blueColor];
        _thirdView.textColor = [UIColor whiteColor];
        _thirdView.text = @"333333333333";
    }
    return _thirdView;
}
  • Next, we need to add the floating constraint layout. Like the Masonry layout, first we need mas_remakeFloatLayoutConstraintsto add a specific layout to each View. Finally, use an array to add a floating layout. The specific code is as follows.
    [self.firstView mas_remakeFloatLayoutConstraints:^(MASConstraintMaker * _Nonnull make, UIView * _Nonnull lastView, UIView * _Nonnull nextView) {
        make.left.equalTo(@50);
        make.height.equalTo(@100);
        make.width.equalTo(@100);
        make.lastFloatConstraint.offset(10.0f);
        make.nextFloatConstraint.offset(-10.0f).priorityHigh();
    }];
    [self.secondView mas_remakeFloatLayoutConstraints:^(MASConstraintMaker * _Nonnull make, UIView * _Nonnull lastView, UIView * _Nonnull nextView) {
        make.left.equalTo(@50);
        make.height.equalTo(@100);
        make.width.equalTo(@100);
        make.lastFloatConstraint.offset(30.0f).priorityLow();
        make.nextFloatConstraint.offset(-10.0f);
    }];
    [self.thirdView mas_remakeFloatLayoutConstraints:^(MASConstraintMaker * _Nonnull make, UIView * _Nonnull lastView, UIView * _Nonnull nextView) {
        make.left.equalTo(@50);
        make.height.equalTo(@100);
        make.lastFloatConstraint.offset(10.0f);
        make.nextFloatConstraint.offset(-10.0f);
    }];

    [@[self.thirdView, self.firstView, self.secondView] mas_remakeFloatLayoutConstraintsWithOrientation:FloatLayoutOrientationBottomToTop needLastConstraint:need];
  • In the above process add constraints, we need to use lastFloatConstraintand nextFloatConstraint, these two properties are derived from MASConstraintMaker + FloatLayout , we can add a spaced relationship with the next View View and by these two attributes.
@property (nonatomic, strong) MASFloatLayoutConstraint *lastFloatConstraint;

@property (nonatomic, strong) MASFloatLayoutConstraint *nextFloatConstraint;
  • Of course, the spacing relationship can also set the priority, in this case. If there is a spacing constraint conflict, set the priority to solve this problem. This is also reflected in the above example.
- (MASFloatLayoutConstraint * (^)(MASLayoutPriority priority))priority;

- (MASFloatLayoutConstraint * (^)(void))priorityLow;

- (MASFloatLayoutConstraint * (^)(void))priorityMedium;

- (MASFloatLayoutConstraint * (^)(void))priorityHigh;
  • mas_remakeFloatLayoutConstraintsThe Block parameter of the method MASConstraintMaker *makewill return the previous view lastViewand the next view in addition to the common ones.Of nextViewcourse, these two parameters may be nil.
typedef void(^FloatConstraintMaker)(MASConstraintMaker *make, UIView *lastView, UIView *nextView);
  • After the constraints of each view are added, we add the floating layout. The floating layout is based on NSArraythe classification NSArray+FloatLayout. The mas_remakeFloatLayoutConstraintsWithOrientationmethod has a total of two parameters. One is to set the direction of the float, and the other is to set the last view and the parent. The relationship between views is similar to the second case mentioned in the previous module.
typedef enum : NSUInteger {
    FloatLayoutOrientationUnknow,      // 未知或者根据自定义的约束进行约束布局
    FloatLayoutOrientationLeftToRight, // 从左到右进行布局
    FloatLayoutOrientationRightToLeft, // 从右到左进行布局
    FloatLayoutOrientationTopToBottom, // 从上到下进行布局
    FloatLayoutOrientationBottomToTop, // 从下到上进行布局
} FloatLayoutOrientation;

/// 执行浮动布局
/// @param orientation 相对于父视图的浮动方向
/// @param needLastConstraint 是否需要添加最后的约束
- (void)mas_remakeFloatLayoutConstraintsWithOrientation:(FloatLayoutOrientation)orientation
                                     needLastConstraint:(BOOL)needLastConstraint;

The overall usage process is like this. On the whole, there is not much difference from the original Masonry layout code. For specific examples, please see my Demo .


to sum up


If you encounter any problems during use, please feel free to contact me, Sao Dong is here to thank you all.

MasonryFloatLayout portal

Guess you like

Origin blog.csdn.net/qq_33591200/article/details/115291655