iOS自定義控制元件:精簡的底部彈框

一個孤獨的搬碼猿發表於2019-03-05

鎮樓圖
在下小白一個,準備寫寫和自己開發息息相關的控制元件,希望大家批評指正。這是第一個常用的底部彈窗,使用了最簡單的分裝方式。

1、標頭檔案裡面宣告瞭一個代理和初始化方法。

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@class LZAlterView;

@protocol LZAlterViewDelegate <NSObject>

- (void)alterView:(LZAlterView *)alterView didSelectedAtIndex:(NSInteger)index;

@end

@interface LZAlterView : UIView

+ (instancetype)alter;

/**
 顯示樣式一

 @param mainTitle 主標題
 @param subTitle 副標題
 @param actionTitleArray 事件按鈕陣列
 @param cancelTitle 取消按鈕
 @return return value description
 */
- (LZAlterView *)configureWithMainTitle:(NSString *)mainTitle subTitle:(NSString *)subTitle actionTitleArray:(NSArray<NSString *> *)actionTitleArray cancelActionTitle:(NSString *)cancelTitle;

/**
 顯示樣式二

 @param mainTitle 主標題
 @param subTitle 副標題
 @param actionTitleArray 事件按鈕組數
 @return return value description
 */
- (LZAlterView *)configureWithMainTitle:(NSString *)mainTitle subTitle:(NSString *)subTitle actionTitleArray:(NSArray<NSString *> *)actionTitleArray;

/**
 顯示樣式三

 @param actionTitleArray 事件按鈕陣列
 @param cancelTitle 取消按鈕
 @return return value description
 */
- (LZAlterView *)configureWithActionTitleArray:(NSArray<NSString *> *)actionTitleArray cancelActionTitle:(NSString *)cancelTitle;

/**
 顯示樣式四

 @param actionTitleArray 事件按鈕陣列
 @return return value description
 */
- (LZAlterView *)configureWithActionTitleArray:(NSArray<NSString *> *)actionTitleArray;

/**
 設定代理

 @param delegate 代理
 @return return value description
 */
- (LZAlterView *)setupDelegate:(id<LZAlterViewDelegate>)delegate;

- (void)showAlter;

@end

NS_ASSUME_NONNULL_END
複製程式碼

2、實現檔案裡面進行頁面的繪製、資料處理和事件處理。

#import "LZAlterView.h"

#define alter_ScreenWidth       UIScreen.mainScreen.bounds.size.width
#define alter_ScreenHeight      UIScreen.mainScreen.bounds.size.height

#define alter_actionHeight      45 * alter_ScreenWidth/375.0
#define alter_cancelSpece       5 * alter_ScreenWidth/375.0
#define alter_mainTitleHeight   alter_actionHeight + alter_cancelSpece
#define alter_FontSize(s)       [UIFont systemFontOfSize:s * alter_ScreenWidth/375.0]

@interface LZAlterView ()

@property (nonatomic) CGRect                         conterViewRect;
@property (nonatomic, strong) UIView                *contanerView;
@property (nonatomic, weak) id <LZAlterViewDelegate> delegate;

@end

@implementation LZAlterView

+ (instancetype)alter {
    static dispatch_once_t onceToken;
    static LZAlterView *alter;
    dispatch_once(&onceToken, ^{
        alter = [[LZAlterView alloc] init];
    });
    return alter;
}

- (instancetype)init {
    self = [super init];
    if (self) {
        UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGestureRecognizer:)];
        [self addGestureRecognizer:tap];
    }
    return self;
}

#pragma mark - Configuration method
/** 代理  */
- (LZAlterView *)setupDelegate:(id<LZAlterViewDelegate>)delegate {
    self.delegate = delegate;
    return self;
}

/** Set title, subtitle, events and cancel */
- (LZAlterView *)configureWithMainTitle:(NSString *)mainTitle subTitle:(NSString *)subTitle actionTitleArray:(NSArray<NSString *> *)actionTitleArray cancelActionTitle:(NSString *)cancelTitle {
    CGFloat width           = alter_actionHeight * (actionTitleArray.count + 1) + alter_cancelSpece + alter_mainTitleHeight;
    self.contanerView.frame = CGRectMake(0, [self safeScreenHeight] - width, alter_ScreenWidth, width);
    [self addSubview:self.contanerView];

    UILabel *titleLabel     = [self setupMainTitle:mainTitle subTitle:subTitle];
    titleLabel.frame        = CGRectMake(0, 0, alter_ScreenWidth, alter_mainTitleHeight);
    [self.contanerView addSubview:titleLabel];

    UIButton *cancelButton  = [self cancelButtonWithTitle:cancelTitle];
    cancelButton.frame      = CGRectMake(0, self.contanerView.frame.size.height - alter_actionHeight, alter_ScreenWidth, alter_actionHeight);
    [self.contanerView addSubview:cancelButton];
    
    for (NSString *name in actionTitleArray) {
        UIButton *button = [self setupActionWithTitle:name];
        NSInteger index  = [actionTitleArray indexOfObject:name];
        button.tag       = 1000 + index;
        button.frame     = CGRectMake(0, titleLabel.frame.size.height + 0.5 + alter_actionHeight * index, alter_ScreenWidth, alter_actionHeight - 0.5);
        [self.contanerView addSubview:button];
    }
    self.conterViewRect = self.contanerView.frame;
    
    return self;
}

/** Set title, subtitle and events */
- (LZAlterView *)configureWithMainTitle:(NSString *)mainTitle subTitle:(NSString *)subTitle actionTitleArray:(NSArray<NSString *> *)actionTitleArray {
    CGFloat width           = alter_actionHeight * actionTitleArray.count + alter_mainTitleHeight;
    self.contanerView.frame = CGRectMake(0, [self safeScreenHeight] - width, alter_ScreenWidth, width);
    [self addSubview:self.contanerView];

    UILabel *titleLabel     = [self setupMainTitle:mainTitle subTitle:subTitle];
    titleLabel.frame        = CGRectMake(0, 0, alter_ScreenWidth, alter_mainTitleHeight);
    [self.contanerView addSubview:titleLabel];
    
    for (NSString *name in actionTitleArray) {
        UIButton *button = [self setupActionWithTitle:name];
        NSInteger index  = [actionTitleArray indexOfObject:name];
        button.tag       = 1000 + index;
        button.frame     = CGRectMake(0, titleLabel.frame.size.height + 0.5 + alter_actionHeight * index, alter_ScreenWidth, alter_actionHeight - 0.5);
        [self.contanerView addSubview:button];
    }
    self.conterViewRect = self.contanerView.frame;
    return self;
}

/** Set events and cancel */
- (LZAlterView *)configureWithActionTitleArray:(NSArray<NSString *> *)actionTitleArray cancelActionTitle:(NSString *)cancelTitle {
    CGFloat width           = alter_actionHeight * (actionTitleArray.count + 1) + alter_cancelSpece;
    self.contanerView.frame = CGRectMake(0, [self safeScreenHeight] - width, alter_ScreenWidth, width);
    [self addSubview:self.contanerView];
    
    UIButton *cancelButton  = [self cancelButtonWithTitle:cancelTitle];
    cancelButton.frame      = CGRectMake(0, self.contanerView.frame.size.height - alter_actionHeight, alter_ScreenWidth, alter_actionHeight);
    [self.contanerView addSubview:cancelButton];
    
    for (NSString *name in actionTitleArray) {
        UIButton *button = [self setupActionWithTitle:name];
        NSInteger index  = [actionTitleArray indexOfObject:name];
        button.tag       = 1000 + index;
        button.frame     = CGRectMake(0, alter_actionHeight * index, alter_ScreenWidth, alter_actionHeight-0.5);
        [self.contanerView addSubview:button];
    }
    self.conterViewRect = self.contanerView.frame;
    return self;
}

/** Set events */
- (LZAlterView *)configureWithActionTitleArray:(NSArray<NSString *> *)actionTitleArray {
    CGFloat width           = alter_actionHeight * actionTitleArray.count;
    self.contanerView.frame = CGRectMake(0, [self safeScreenHeight] - width, alter_ScreenWidth, width);
    [self addSubview:self.contanerView];

    for (NSString *name in actionTitleArray) {
        UIButton *button = [self setupActionWithTitle:name];
        NSInteger index  = [actionTitleArray indexOfObject:name];
        button.tag       = 1000 + index;
        button.frame     = CGRectMake(0, alter_actionHeight * index, alter_ScreenWidth, alter_actionHeight-0.5);
        [self.contanerView addSubview:button];
    }
    self.conterViewRect = self.contanerView.frame;
    return self;
}

/** Event button style */
- (UIButton *)setupActionWithTitle:(NSString *)title {
    UIButton *button       = [UIButton buttonWithType:UIButtonTypeCustom];
    [button setTitle:title forState:UIControlStateNormal];
    [button setTitleColor:UIColor.blackColor forState:UIControlStateNormal];
    button.backgroundColor = [UIColor colorWithRed:0.9 green:0.9 blue:0.9 alpha:1];
    button.titleLabel.font = alter_FontSize(15);
    [button addTarget:self action:@selector(buttonTouched:) forControlEvents:UIControlEventTouchUpInside];
    return button;
}

/** Cancel button style */
- (UIButton *)cancelButtonWithTitle:(NSString *)title {
    UIButton *button       = [UIButton buttonWithType:UIButtonTypeCustom];
    [button setTitle:title forState:UIControlStateNormal];
    [button setTitleColor:[UIColor colorWithRed:0.9 green:0 blue:0 alpha:1] forState:UIControlStateNormal];
    button.backgroundColor = [UIColor colorWithRed:0.9 green:0.9 blue:0.9 alpha:1];
    button.titleLabel.font = alter_FontSize(15);
    [button addTarget:self action:@selector(hidenAlter) forControlEvents:UIControlEventTouchUpInside];

    return button;
}

/** Title style */
- (UILabel *)setupMainTitle:(NSString *)title subTitle:(NSString *)subTitle {
    NSString *titleString;
    
    if (subTitle.length == 0) {
        titleString = [NSString stringWithFormat:@"%@",title];
    } else {
        titleString = [NSString stringWithFormat:@"%@\n%@",title,subTitle];
    }
    
    UILabel *label                         = [[UILabel alloc] init];
    label.backgroundColor                  = [UIColor colorWithRed:0.9 green:0.9 blue:0.9 alpha:1];
    label.textColor                        = UIColor.blackColor;
    label.font                             = alter_FontSize(15);
    NSMutableAttributedString *muAttribute = [[NSMutableAttributedString alloc] initWithString:titleString];

    [muAttribute addAttribute:NSFontAttributeName value:alter_FontSize(12) range:NSMakeRange(titleString.length - subTitle.length, subTitle.length)];
    [muAttribute addAttribute:NSForegroundColorAttributeName value:UIColor.grayColor range:NSMakeRange(titleString.length - subTitle.length, subTitle.length)];

    label.textAlignment                    = NSTextAlignmentCenter;
    label.numberOfLines                    = 2;
    label.attributedText                   = muAttribute;
    return label;
}

#pragma mark - Button click events and delegate

- (void)buttonTouched:(UIButton *)sender {
    [self hidenAlter];
    if (self.delegate && [self.delegate respondsToSelector:@selector(alterView:didSelectedAtIndex:)]) {
        [self.delegate alterView:self didSelectedAtIndex:sender.tag - 1000];
    }
}

/** iOS 11 safeArea bottom space */
- (CGFloat)safeScreenHeight {
    CGFloat safeAreaInsetsBottom = 0;
    if (@available(iOS 11.0, *)) {
        safeAreaInsetsBottom = UIApplication.sharedApplication.delegate.window.safeAreaInsets.bottom;
    } else {
        safeAreaInsetsBottom = 0;
    }
    return alter_ScreenHeight - safeAreaInsetsBottom;
}

#pragma mark - Show alter And hiden alter
/** Show alter from bottom */
- (void)showAlter {
    [UIApplication.sharedApplication.delegate.window addSubview:self];
    self.backgroundColor = [UIColor colorWithWhite:0 alpha:0.3];
    self.frame           = CGRectMake(0, 0, alter_ScreenWidth, alter_ScreenHeight);
    self.alpha           = 0;
    self.contanerView.frame = CGRectMake(0, UIScreen.mainScreen.bounds.size.height, UIScreen.mainScreen.bounds.size.height, 0);
    
    [UIView animateWithDuration:0.4 animations:^{
        self.alpha              = 1;
        self.contanerView.frame = self.conterViewRect;
    }];
}

/** Hiden alter and remove subViews from superview */
- (void)hidenAlter {
    [UIView animateWithDuration:0.4 animations:^{
        self.alpha              = 0;
        self.contanerView.frame = CGRectMake(0, UIScreen.mainScreen.bounds.size.height, UIScreen.mainScreen.bounds.size.height, 0);
    } completion:^(BOOL finished) {
        for (UIView *view in self.contanerView.subviews) {
            [view removeFromSuperview];
        }
        [self removeFromSuperview];
    }];
}

#pragma mark - Getter

- (UIView *)contanerView {
    if (!_contanerView) {
        _contanerView                 = [[UIView alloc] init];
        _contanerView.backgroundColor = [UIColor colorWithRed:0.9 green:0.9 blue:0.9 alpha:0.5];
    }
    return _contanerView;
}

#pragma mark - TapGestureRecognizer event

- (void)tapGestureRecognizer:(id)sender {
    [self hidenAlter];
}

@end

複製程式碼

3、使用方法,遵循LZAlterViewDelegate

樣式一

//  Objc
[[[[LZAlterView alter] configureWithMainTitle:@"選擇圖片"
                                                 subTitle:@"1"
                                         actionTitleArray:@[@"開啟相簿", @"開啟相機"]
                                        cancelActionTitle:@"取消"] setupDelegate:self] showAlter];
//  Swift
LZAlterView.alter().configure(withMainTitle: "選擇圖片", subTitle: "副標題", actionTitleArray: ["相機", "相簿"], cancelActionTitle: "取消").setupDelegate(self).showAlter()
複製程式碼

樣式二

//  Objc
 [[[[LZAlterView alter] configureWithMainTitle:@"選擇圖片"
                                                 subTitle:@"2"
                                         actionTitleArray:@[@"開啟相簿", @"開啟相機"]] setupDelegate:self] showAlter];
//  Swift
LZAlterView.alter().configure(withMainTitle: "選擇圖片", subTitle: "需要使用者許可權", actionTitleArray: ["相機", "相簿"]).setupDelegate(self).showAlter()
複製程式碼

樣式三

//  Objc
[[[[LZAlterView alter] configureWithActionTitleArray:@[@"開啟相簿", @"開啟相機"]
                                               cancelActionTitle:@"取消"] setupDelegate:self] showAlter];
//  Swift
LZAlterView.alter().configure(withActionTitleArray: ["相機", "相簿"], cancelActionTitle: "取消").setupDelegate(self).showAlter()
複製程式碼

樣式四

//  Objc
[[[[LZAlterView alter] configureWithActionTitleArray:@[@"開啟相簿", @"開啟相機", @"拍照視訊"]] setupDelegate:self] showAlter];
//  Swift
LZAlterView.alter().configure(withActionTitleArray: ["相機", "相簿"]).setupDelegate(self).showAlter()
複製程式碼

4、代理方法

// Objc
- (void)alterView:(LZAlterView *)alterView didSelectedAtIndex:(NSInteger)index {
    NSLog(@"點選第%ld按鈕", index);
}
// Swift
extension ViewController: LZAlterViewDelegate {
    func alterView(_ alterView: LZAlterView, didSelectedAt index: Int) {
        print("點選第\(index)按鈕")
    }
}
複製程式碼

5、效果圖

效果圖.gif

結尾:有問題希望大家指正!???

相關文章