preface

This is the fourth installment in the once and For All iOS custom View packaging series, which aims to provide ideas for packaging. The results are important, but the process is best understood. It is better to teach a man to fish than to give him fish. ⚠️ article aims to help the low degree of encapsulation of friends, god can ignore do not spray.

List of links to historical articles:
  • Once and for all, iOS bootmask encapsulates the process
  • Once and for all, iOS Web View Controller encapsulates the process
  • Once and for all, iOS multiple popover packaging process
The body of the

Recently updated project requirements, the navigation selection module needs to be reconstructed, so the packaging process is shared, and the effect picture is as follows:





Navigation selects popovers

According to the renderings, it can be seen that the title bar position needs to be customized, and the text styles such as option position can be adjusted, so UIActionSheet or UIAlertController of the system cannot be used for implementation. The view needs to be customized, and the ActionSheet will be used in other functional modules in the future considering applicable scenarios. So encapsulate it as a generic class.

Moving on to requirements, what controls would be better to use as the body? Yes, UITableView is a perfect fit, as shown in the graph below:





At figure

The advantage of the above design is that you can pass a custom View into the TableView’s tableHeaderView. If you need to customize other styles for the option position, you can customize the Cell. Therefore, it can meet various scenarios of customized ActionSheet.

According to the planing diagram, first we create the maskView and the main TableView respectively. The thing to notice here is that in order for this to work we need to make the color of our TableView transparent.

_tableView.backgroundColor = [UIColor clearColor];Copy the code

And we want to split the TableView into two groups, namely the body option and the cancel button, so set the proxy method:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 2;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return (section == 0)?_optionsArr.count: 1.;
}Copy the code

Next, work on the rounded corners of the view. When you set layer. CornerRadius, the cornerRadius will look like this:





rendering

It’s obviously ugly and the design will crash, so what we need to do is to round only the last item in the selection, the Amap option in the picture, and to achieve the bottom left and right corner only for aesthetic effect.

Here we need to implement it with UIBezierPath and CAShapeLayer. Check whether it is the last option and set it as follows.

UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:
                                      cell.contentView.bounds byRoundingCorners:
                                      UIRectCornerBottomLeft|UIRectCornerBottomRight cornerRadii:
                                      CGSizeMake(10.10)];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc]init];
maskLayer.frame = cell.contentView.bounds;
maskLayer.path = maskPath.CGPath;
cell.layer.mask = maskLayer;Copy the code

ByRoundingCorners is used to set the parameters for processing edges and corners. There are the following enumerators to choose from:

typedef NS_OPTIONS(NSUInteger.UIRectCorner) {
    UIRectCornerTopLeft= 1 < < 0,UIRectCornerTopRight= 1 < < 1,UIRectCornerBottomLeft1 < < = 2.UIRectCornerBottomRight= 1 < < 3,UIRectCornerAllCorners  = ~0UL
};Copy the code

Such as set byRoundingCorners to UIRectCornerTopLeft | UIRectCornerBottomRight, namely the upper left and lower right setting, the effect of the View is:





rendering

After the above adjustment, the rounded corner effect of the view is complete, and finally set the transparent view of the group tail:

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
    return SPACE;
}

- (UIView*)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
    UIView *footerView = [[UIView alloc]initWithFrame:CGRectMake(0.0, tableView.bounds.size.width, SPACE)];
    footerView.backgroundColor = [UIColor clearColor];
    return footerView;
}Copy the code

Once done, the basic UI effects are complete.

Next, let’s consider the leakage method problem, which simply simulates UIActionSheet creation and exposes methods:

- (instancetype)initWithTitleView:(UIView*)titleView
                       optionsArr:(NSArray*)optionsArr
                      cancelTitle:(NSString*)cancelTitle
                    selectedBlock:(void(^)(NSInteger))selectedBlock
                      cancelBlock:(void((^)))cancelBlock;Copy the code

Call as follows:

SureCustomActionSheet *optionsView = [[SureCustomActionSheet alloc]initWithTitleView:self.headView optionsArr:self.dataArr cancelTitle:@"Cancel" selectedBlock:^(NSInteger index) {

} cancelBlock:^{

}];
[self.view addSubview:optionsView];Copy the code

This will pass in the desired header view, cancel text, process option events, and so on.

Finally, simply give the view the effect of show and hide, and when the time is not appropriate to call, and here we need to adjust the height of the TableView to adapt to the height of the content contained.

- (void)show {
    _tableView.frame = CGRectMake(SPACE, Screen_height, Screen_Width - (SPACE * 2), _tableView.rowHeight * (_optionsArr.count + 1) + _headView.bounds.size.height + (SPACE * 2));
    [UIView animateWithDuration:.5 animations:^{
        CGRect rect = _tableView.frame;
        rect.origin.y -= _tableView.bounds.size.height;
        _tableView.frame = rect;
    }];
}

- (void)dismiss {
    [UIView animateWithDuration:.5 animations:^{
        CGRect rect = _tableView.frame;
        rect.origin.y += _tableView.bounds.size.height;
        _tableView.frame = rect;
    } completion:^(BOOL finished) {
        [self removeFromSuperview];
    }];
}Copy the code
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    [self dismiss];
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [self dismiss];
}Copy the code

So far, the effect of the requirement has been basically completed, the above excerpted part of the code, demo has been uploaded to Github, need to download. Once and for all, iOS custom ActionSheet packaging process Demo 🔗

Temporarily write here, like you can point a like or follow me, than the heart (. · ω ·. ノ ♡