本人有若干成套学习视频, 可试看! 可试看! 可试看, 重要的事情说三遍 包含Java
, 数据结构与算法
, iOS
, 安卓
, python
, flutter
等等, 如有需要, 联系微信tsaievan
.
现在大多数APP都不再使用系统原生的AlertController了, 而是自定义AlertView控件, 比如.
系统的AlertActionSheet是这样的:
但比如微信, 自定义的AlertActionSheet是这样的:
那么我们如何来仿写一个类似于微信这样的AlertActionSheet呢?
如上图所示, 红色部分是整个的AlertActionSheet,它和屏幕一样大, 当我点击某个按钮弹出AlertActionSheet的时候, 会出现蒙版的效果, 绿色的部分是AlertActionSheet的contentView, 蓝色部分是各个按钮. 它们的frame值大小也决定了contentView的frame值.
基本思路是先创建各个button(不包括cancelButton),然后将这些button组成一个数组, 作为参数传入到AlertActionSheet的初始化方法中, 然后在layoutSubviews 方法中对contentView, 以及contentView的子控件进行布局
typedef void(^YFActionSheetHandler)(YFActionSheet *actionSheet, YFActionButton *actionButton);
/**
初始化方法:不带图片,按钮标题颜色黑色
@param title 按钮标题
@param handler 按钮点击处理事件
@return 返回一个自定义的actionButton
*/
- (instancetype)initWithTitle:(NSString *)title handler:(YFActionSheetHandler)handler;
初始化button成功之后, 将这些button作为数组传入AlertActionSheet的初始化方法中
/**
初始化方法(不包含标题和副标题)
@param actionButtons ActionSheet的按钮数组
@return 返回一个自定义的ActionSheet
*/
- (instancetype)initWithActionButtons:(NSArray *)actionButtons;
初始化方法的实现是这样的:
- (instancetype)initWithTitle:(NSString *)title subtitle:(NSString *)subtitle actionButtons:(NSArray *)actionButtons {
if (self = [super init]) {
_title = title;
_subtitle = subtitle;
self.actionButtons = actionButtons.mutableCopy;
[self commonInit];
}
return self;
}
- (instancetype)initWithActionButtons:(NSArray *)actionButtons {
return [self initWithTitle:nil subtitle:nil actionButtons:actionButtons];
}
设置UI
- (void)commonInit {
// 这个步骤相当于设置蒙版
self.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.1];
[self addSubview:self.contentView];
[self.contentView addSubview:self.cancelButton];
[self addGestureRecognizer:self.tapGesture];
if (!self.actionButtons) {
return;
}
// 设置一些基本的属性
_cancelButtonHeight = 44;
_cancelButtonTopOrBottomMargin = 6;
_leftOrRightMargin = 20;
_seperatorHeight = 0.5;
[self setupHeadView];
[self.actionButtons enumerateObjectsUsingBlock:^(YFActionButton * _Nonnull actionButton, NSUInteger idx, BOOL * _Nonnull stop) {
[actionButton addTarget:self action:@selector(actionButtonDidClickAction:) forControlEvents:UIControlEventTouchUpInside];
[self.contentView addSubview:actionButton];
}];
}
- (void)setupHeadView {
CGFloat labelMaxWidth = [UIScreen mainScreen].bounds.size.width - 2 * _leftOrRightMargin;
if (_title) { // 有标题才设置标题
self.titleLabel.text = _title;
[self.headView addSubview:self.titleLabel];
_titleHeight = [_title boundingRectWithSize:CGSizeMake(labelMaxWidth, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:self.titleLabel.font} context:nil].size.height;
_headViewHeight += (YFTopMargin + _titleHeight);
}
if (_subtitle) {
self.subtitleLabel.text = _subtitle;
[self.headView addSubview:self.titleLabel];
_subtitleHeight = [_subtitle boundingRectWithSize:CGSizeMake(labelMaxWidth, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName :self.subtitleLabel.font} context:nil].size.height;
_headViewHeight += (YFTopMargin + _subtitleHeight);
}
if (_headView) {
[self.contentView addSubview:_headView];
}
_headViewHeight = MAX(0, _headViewHeight);
}
在layoutSubviews方法中, 我需要设置headView的frame值, 如果有headView的话, 还需要设置各个button的frame值
- (void)layoutSubviews {
[super layoutSubviews];
CGFloat contentViewWidth = kYFActionSheetWidth - 2 *_leftOrRightMargin;
if (_headView) {
self.headView.frame = CGRectMake(0, 0, contentViewWidth, _headViewHeight);
if (_titleLabel && _subtitleLabel) {
self.titleLabel.frame = CGRectMake(0, YFTopMargin, contentViewWidth, _titleHeight);
self.subtitleLabel.frame = CGRectMake(0, CGRectGetMaxY(_titleLabel.frame) + YFTopMargin / 2, contentViewWidth, _subtitleHeight);
}else if (_titleLabel) {
self.titleLabel.frame = self.headView.bounds;
}else if (_subtitleLabel) {
self.subtitleLabel.frame = self.headView.bounds;
}
}
CGFloat buttonY = _headViewHeight;
for (NSUInteger i = 0; i < self.actionButtons.count; i++) {
YFActionButton *actionButton = self.actionButtons[i];
actionButton.frame = CGRectMake(0, buttonY, contentViewWidth, actionButton.buttonHeight);
buttonY += _seperatorHeight + actionButton.buttonHeight;
self.cancelButton.frame = CGRectMake(0, buttonY + _cancelButtonTopOrBottomMargin, contentViewWidth, _cancelButtonHeight);
}
}
那么在使用中, 我需要设置alertActionSheet的出现和消失, 所以我需要暴露两个接口供外界使用, 其实hide接口不需要暴露, 因为我对整个alertActionSheet添加了手势, 当手势触发时, 自动隐藏contentView.
- (void)switchToHideState {
self.alpha = 0;
self.contentView.frame = CGRectMake(_leftOrRightMargin, kYFActionSheetHeight, kYFActionSheetWidth - 2 * _leftOrRightMargin, _contentViewHeight);
}
- (void)switchToShowState {
self.alpha = 1;
self.contentView.frame = CGRectMake(_leftOrRightMargin, (kYFActionSheetHeight - _contentViewHeight), kYFActionSheetWidth - 2 * _leftOrRightMargin, _contentViewHeight);
}
- (void)show {
UIWindow *window = [UIApplication sharedApplication].keyWindow;
self.frame = window.bounds;
[window addSubview:self];
[self getContentViewHeight];
[self switchToHideState];
WEAK_SELF
[UIView animateWithDuration:0.25 animations:^{
[weakSelf switchToShowState];
} completion:nil];
}
- (void)hide {
WEAK_SELF
[UIView animateWithDuration:0.25 animations:^{
[weakSelf switchToHideState];
} completion:^(BOOL finished) {
if (finished) {
[weakSelf removeFromSuperview];
}
}];
}
看一下实现效果, 是不是和微信的很像呢?
源码链接