
自定义View实现闹钟列表,左滑删除,滑动列表时收起删除按钮。用代理的方法实现ListView的创建,删除以及开关回调,并实现动画效果。
ClockViewCell使用block通知ListView,ListView通过代理通知上层ClockView
1、文件组成
ClockView一共由3部分组成

(1)ClockView为底层View,仅用于添加ListView,可以自定义ListView中Cell的高度,每个Cell的间距,利用代理实现Cell的个数、创建Cell以及删除等回调。
(2)ClockListView为列表本体,用于承载每一个Cell,管理Cell的各种操作,上下滑动手势收回删除按钮等,对Cell中的block回调进行处理,再通过代理通知ClockView。block包括手势、删除、开关等操作。
(3)Cell作为每一个闹钟内容的载体,实现时间、周期以及闹钟开关展示。对于cell中的手势、以及操作用block的方式通知ClockListView,ClockListView再通知ClockView。Cell中的左滑删除手势为了避免ClockListView的UIScrollView上下滑动冲突,只有横向滑动才处理cell,否则优先响应UIScrollView。
具体看下述代码,大部分均做了注释
2、代码构成
(1)ClockView调用ListView
ClockView.h
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface ClockView : UIView
@end
NS_ASSUME_NONNULL_END
ClockView.m
dataArray由真实情况而定,本文只进行随机展示
#import "ClockView.h"
#import "ClockViewCell.h"
#import "ClockListView.h"
@interface ClockView () <ClockListViewDelegate>
@property (nonatomic, strong) NSMutableArray *dataArray;
@property (nonatomic, strong) ClockListView *listView;
@end
@implementation ClockView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self initView];
}
return self;
}
- (void)initView {
UILabel *topLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 0, self.bounds.size.width - 40, 52)];
topLabel.text = @" 当到达所设置的时间,智能床将缓缓升起,帮你从梦中唤醒。";
topLabel.textColor = [UIColor colorWithRed:111/255.0 green:111/255.0 blue:111/255.0 alpha:1.0];;
topLabel.font = [UIFont systemFontOfSize:18];
topLabel.textAlignment = NSTextAlignmentLeft;
topLabel.numberOfLines = 0;
[self addSubview:topLabel];
[self addSubview:self.listView];
[self reloadData];
}
- (void)reloadData {
self.dataArray = [NSMutableArray array];
// Example data loading
for (int i = 0; i < 10; i++) {
[self.dataArray addObject:@(i)];
}
[self.listView reloadData];
}
#pragma mark - delegate
- (NSInteger)numberOfCellsInClockListView:(ClockListView *)clockListView {
return self.dataArray.count;
}
- (ClockViewCell *)clockListView:(ClockListView *)clockListView cellForRowAtIndex:(NSInteger)index {
// ClockViewCell *cell = [clockListView dequeueReusableCellWithIdentifier:@"ClockCell"];
ClockViewCell *cell = [clockListView dequeueCell];
NSString *time = [NSString stringWithFormat:@"10:%02ld", index];
[cell configureWithTime:time repeat:@"周一 周二" on:YES];
return cell;
}
- (void)clockListView:(ClockListView *)clockListView didDeleteClockAtIndex:(NSInteger)index {
[self.dataArray removeObjectAtIndex:index];
// [clockListView reloadData];
}
- (void)clockListView:(ClockListView *)clockListView didClickClockAtIndex:(NSInteger)index {
// Handle clock click event
NSLog(@"Clock at index %ld clicked", (long)index);
}
- (void)clockListView:(ClockListView *)clockListView didSwitchClockAtIndex:(NSInteger)index on:(BOOL)on {
// Handle switch state change
NSLog(@"Clock at index %ld switched %@", (long)index, on ? @"ON" : @"OFF");
}
#pragma mark - lazy load
- (ClockListView *)listView {
if (!_listView) {
_listView = [[ClockListView alloc] initWithFrame:CGRectMake(0, 72, self.bounds.size.width, self.bounds.size.height - 72)];
_listView.backgroundColor = [UIColor colorWithRed:242/255.0 green:243/255.0 blue:245/255.0 alpha:1.0];;
_listView.delegate = self;
_listView.cellHeight = 96;//96
_listView.cellSpacing = 20;//20
}
return _listView;
}
@end
(2)ListView
ClockListView.h
创建代理以供使用,暂未实现cell回收机制
#import <UIKit/UIKit.h>
#import "ClockViewCell.h"
NS_ASSUME_NONNULL_BEGIN
@class ClockListView;
@protocol ClockListViewDelegate <NSObject>
@optional
/// 删除cell的回调
/// - Parameters:
/// - clockListView: clockListView
/// - index: index
- (void)clockListView:(ClockListView *)clockListView didDeleteClockAtIndex:(NSInteger)index;
@optional
/// 点击cell的回调
/// - Parameters:
/// - clockListView: clockListView
/// - index: index
- (void)clockListView:(ClockListView *)clockListView didClickClockAtIndex:(NSInteger)index;
@optional
- (void)clockListView:(ClockListView *)clockListView didSwitchClockAtIndex:(NSInteger)index on:(BOOL)on;
/// 设置cell的样式
/// - Parameters:
/// - clockListView: clockListView
/// - index: index
- (ClockViewCell *)clockListView:(ClockListView *)clockListView cellForRowAtIndex:(NSInteger)index;
/// 设置cell的个数
/// - Parameter clockListView: clockListView
- (NSInteger)numberOfCellsInClockListView:(ClockListView *)clockListView;
@end
@interface ClockListView : UIView
/// 代理
@property (nonatomic, weak) id<ClockListViewDelegate> delegate;
/// cell的高度
@property (nonatomic, assign) CGFloat cellHeight;
/// 每个cell之间的间距
@property (nonatomic, assign) CGFloat cellSpacing;
- (ClockViewCell *)dequeueCell;
//- (ClockViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier;
- (void)reloadData;
@end
NS_ASSUME_NONNULL_END
ClockListView.m
#import "ClockListView.h"
@interface ClockListView () <UIScrollViewDelegate>
@property (nonatomic, strong) UIScrollView *scrollView;
@property (nonatomic, strong) NSMutableArray<ClockViewCell *> *cellArray;
//@property (nonatomic, strong) NSMutableSet<ClockViewCell *> *reusableCells;
@property (nonatomic, assign) NSInteger rowCount;
@property (nonatomic, assign) NSInteger cellIndex;
@end
@implementation ClockListView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.rowCount = 0;
self.cellHeight = 96;
self.cellSpacing = 20;
// self.reusableCells = [NSMutableSet set];
[self config];
}
return self;
}
- (ClockViewCell *)dequeueCell {
ClockViewCell *cell = self.cellArray[self.cellIndex];
return cell;
}
// 提供重用 cell,暂未实现
//- (ClockViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier {
// ClockViewCell *cell = [self.reusableCells anyObject];
// if (cell) {
// [self.reusableCells removeObject:cell];
// } else {
// cell = [[ClockViewCell alloc] initWithReuseIdentifier:identifier];
// }
// return cell;
//}
- (void)config {
if ([self.delegate respondsToSelector:@selector(numberOfCellsInClockListView:)]) {
self.rowCount = [self.delegate numberOfCellsInClockListView:self];
[self initView];
}
}
- (void)initView {
self.cellArray = [NSMutableArray array];
for (int i = 0; i < self.rowCount; i++) {
ClockViewCell *cell = [[ClockViewCell alloc] initWithFrame:CGRectMake(0, (self.cellSpacing + self.cellHeight) * i, self.bounds.size.width, self.cellHeight)];
[cell configureWithTime:@"10:21" repeat:@"周一 周二" on:YES];
[self.scrollView addSubview:cell];
[self.cellArray addObject:cell];
__weak typeof(self) weakSelf = self;
cell.deleteBlock = ^(ClockViewCell *cell) {
NSInteger index = [weakSelf.cellArray indexOfObject:cell];
[weakSelf deleteCellAtIndex:index];
};
cell.switchBlock = ^(ClockViewCell *cell, BOOL on) {
// 处理开关状态变化
NSInteger index = [weakSelf.cellArray indexOfObject:cell];
if ([weakSelf.delegate respondsToSelector:@selector(clockListView:didSwitchClockAtIndex:on:)]) {
[weakSelf.delegate clockListView:weakSelf didSwitchClockAtIndex:index on:on];
}
};
cell.tapBlock = ^(ClockViewCell *cell) {
// 处理点击事件
[weakSelf cellTapActoin:cell];
};
cell.panBlock = ^(ClockViewCell *cell) {
// 处理滑动手势
[weakSelf closeAllExcept:cell];
};
if ([self.delegate respondsToSelector:@selector(clockListView:cellForRowAtIndex:)]) {
self.cellIndex = i;
[self.delegate clockListView:self cellForRowAtIndex:i];
}
}
self.scrollView.contentSize = CGSizeMake(self.bounds.size.width, (self.cellSpacing + self.cellHeight) * self.cellArray.count);
}
- (void)reloadData {
// 清空之前的 cell
for (ClockViewCell *cell in self.cellArray) {
// [self.reusableCells addObject:cell];
[cell removeFromSuperview];
}
[self.cellArray removeAllObjects];
// 重新加载数据
[self config];
}
#pragma mark - UIScollerView delegate
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
[self closeAllSwipeCells];
}
#pragma mark - methods
/// 删除 cell
/// - Parameter index: cell 的索引
- (void)deleteCellAtIndex:(NSInteger)index {
if (index >= self.cellArray.count) return;
// 判断最后一个 cell 是否可见
BOOL isLastCellVisible = [self isLastCellVisible];
NSInteger count = self.cellArray.count;
ClockViewCell *cellToDelete = self.cellArray[index];
// 移除数据源
[self.cellArray removeObjectAtIndex:index];
// 更新后续 cell 的位置,如果cell不是最后一个,后面的cell要实现上移
for (NSInteger i = index; i < count - 1; i++) {
ClockViewCell *cell = self.cellArray[i];
[UIView animateWithDuration:0.3
delay:0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
CGRect frame = cell.frame;
frame.origin.y -= (self.cellHeight + self.cellSpacing);
cell.frame = frame;
} completion:nil];
}
// 淡出动画
[UIView animateWithDuration:0.3
delay:0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
//当cell为最后一项时
if (index == count - 1) {
CGRect frame = cellToDelete.frame;
frame.origin.y += self.cellHeight;
cellToDelete.frame = frame;
}
cellToDelete.alpha = 0.0;
} completion:^(BOOL finished) {
[cellToDelete removeFromSuperview];
// 判断最后一个cell是否可见,如果最后一个cell可见,当删除任意一个cell时,底部会出现空虚,contentsize变化时会导致画面突然往下坠,所以先滚到视图最下面再修改contentSize
if (isLastCellVisible) {
//删除的是最后一个cell
CGFloat offsetY = MAX(self.scrollView.contentSize.height - self.scrollView.bounds.size.height - (self.cellSpacing + self.cellHeight), 0);
[self.scrollView setContentOffset:CGPointMake(0, offsetY) animated:YES];
//延迟0.3秒,这里延迟0.3秒是为了让动画完成后再更新 contentSize
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.scrollView.contentSize = CGSizeMake(self.bounds.size.width, (self.cellSpacing + self.cellHeight) * (count - 1));
});
} else {
self.scrollView.contentSize = CGSizeMake(self.bounds.size.width, (self.cellSpacing + self.cellHeight) * (count - 1));
}
}];
// 通知代理
if ([self.delegate respondsToSelector:@selector(clockListView:didDeleteClockAtIndex:)]) {
[self.delegate clockListView:self didDeleteClockAtIndex:index];
}
}
/// 是否最后一个 cell 可见
- (BOOL)isLastCellVisible {
if (self.cellArray.count == 0) return NO;
ClockViewCell *lastCell = self.cellArray.lastObject;
CGRect cellFrameInScrollView = lastCell.frame;
CGRect visibleRect = CGRectMake(self.scrollView.contentOffset.x,
self.scrollView.contentOffset.y,
self.scrollView.bounds.size.width,
self.scrollView.bounds.size.height);
return CGRectIntersectsRect(cellFrameInScrollView, visibleRect);
}
- (void)closeAllExcept:(ClockViewCell *)exceptCell {
for (ClockViewCell *cell in self.cellArray) {
if (cell != exceptCell) {
[cell closeSwipe];
}
}
}
- (void)closeAllSwipeCells {
for (ClockViewCell *cell in self.cellArray) {
if (cell.isOpen) {
[cell closeSwipe];
}
}
}
- (void)cellTapActoin:(ClockViewCell *)currentCell {
if (currentCell.isOpen) {
[currentCell closeSwipe];
return;
}
// 检查是否有其他 cell 是打开的
BOOL isOpen = NO;
for (ClockViewCell *cell in self.cellArray) {
if (cell != currentCell && cell.isOpen) {
isOpen = YES;
break;
}
}
// 如果没有其他 cell 是打开的,执行点击事件,否则关闭其他 cell
if (isOpen) {
[self closeAllExcept:currentCell];
} else {
// 通知代理
if ([self.delegate respondsToSelector:@selector(clockListView:didClickClockAtIndex:)]) {
NSInteger index = [self.cellArray indexOfObject:currentCell];
if (index != NSNotFound) {
[self.delegate clockListView:self didClickClockAtIndex:index];
}
}
}
}
#pragma mark - lazy load
- (UIScrollView *)scrollView {
if (!_scrollView) {
_scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height)];
_scrollView.backgroundColor = [UIColor colorWithRed:242/255.0 green:243/255.0 blue:245/255.0 alpha:1.0];;
_scrollView.showsVerticalScrollIndicator = NO;
_scrollView.showsHorizontalScrollIndicator = NO;
_scrollView.contentSize = CGSizeMake(self.bounds.size.width, self.bounds.size.height);
_scrollView.delegate = self;
[self addSubview:_scrollView];
}
return _scrollView;
}
@end
(3)Cell
ClockViewCell.h
使用blcck通知Listview
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface ClockViewCell : UIView
/// 更新cell数据
/// - Parameters:
/// - time: 时间
/// - repeat: 重复日期
/// - on: 开关状态
- (void)configureWithTime:(NSString *)time repeat:(NSString *)repeat on:(BOOL)on;
/// 删除按钮是否跟随视图滑动,默认YES
@property (nonatomic, assign) BOOL isFollowing;
/// 是否为打开状态,默认NO
@property (nonatomic, assign) BOOL isOpen;
/// cell的高度
@property (nonatomic, assign) CGFloat cellHeight;
/// 每个cell之间的间距
@property (nonatomic, assign) CGFloat cellSpacing;
//- (void)openSwipe;
/// 恢复视图位置
- (void)closeSwipe;
//@property (nonatomic, copy, readonly) NSString *reuseIdentifier;
//- (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier;
@property (nonatomic, copy) void(^deleteBlock)(ClockViewCell *cell);
@property (nonatomic, copy) void(^switchBlock)(ClockViewCell *cell, BOOL on);
@property (nonatomic, copy) void(^tapBlock)(ClockViewCell *cell);
@property (nonatomic, copy) void(^panBlock)(ClockViewCell *cell);
@end
NS_ASSUME_NONNULL_END
ClockViewCell.h
#import "ClockViewCell.h"
@interface ClockViewCell () <UIGestureRecognizerDelegate>
@property (nonatomic, assign) CGFloat contentViewX;
@property (nonatomic, strong) UIView *bgView;
@property (nonatomic, strong) UIView *contentView;
@property (nonatomic, strong) UILabel *timeLabel;
@property (nonatomic, strong) UILabel *repeatLabel;
@property (nonatomic, strong) UISwitch *switchView;
@property (nonatomic, strong) UIButton *deleteBtn;
@end
@implementation ClockViewCell
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.isOpen = NO;
[self initView];
[self addGesture];
}
return self;
}
//- (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier {
// if (self = [super initWithFrame:CGRectZero]) {
// _reuseIdentifier = [reuseIdentifier copy];
// // 在这里加 subviews
//
// self.isOpen = NO;
// [self initView];
// [self addGesture];
// }
// return self;
//}
- (void)addGesture {
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
pan.cancelsTouchesInView = NO;
pan.delegate = self;
[self.contentView addGestureRecognizer:pan];
//点击手势
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
[self.contentView addGestureRecognizer:tap];
}
#pragma mark - UIGestureRecognizerDelegate
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return NO;
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
UIPanGestureRecognizer *pan = (UIPanGestureRecognizer *)gestureRecognizer;
CGPoint translation = [pan translationInView:self];
return fabs(translation.x) > fabs(translation.y); // 横向滑动才触发 cell 滑动
}
return YES;
}
- (void)handleTap:(UITapGestureRecognizer *)gesture {
if (self.isOpen) {
[self closeSwipe];
return;
}
if (self.tapBlock) {
self.tapBlock(self);
}
}
#pragma mark - 手势处理
- (void)handlePan:(UIPanGestureRecognizer *)gesture {
if (self.panBlock) {
self.panBlock(self);
}
CGPoint translation = [gesture translationInView:self];
static CGFloat originalX = 0;
static CGFloat originalXOfBtn = 0;
if (gesture.state == UIGestureRecognizerStateBegan) {
originalX = self.contentView.frame.origin.x;// 记录初始位置
originalXOfBtn = self.deleteBtn.frame.origin.x;
} else if (gesture.state == UIGestureRecognizerStateChanged) {
CGFloat newX = 0;
CGFloat effectWidth = 0;//为了让按钮多一个滑动过多时的回弹效果,显得不那么生硬,根据具体需要增加
if (originalX == self.contentViewX) {
// 如果初始位置为self.contentViewX,则允许向左滑动
newX = MIN(0 + effectWidth, MAX(-76 - effectWidth, translation.x));// 限制左滑范围默认 0 到 -76
} else {
newX = MIN(76 + effectWidth, MAX(0 - effectWidth, translation.x));// 限制左滑范围默认 0 到 76
}
self.contentView.frame = CGRectMake(newX + originalX, 0, self.bounds.size.width - 40, self.bounds.size.height);
if (self.isFollowing) {
self.deleteBtn.frame = CGRectMake(originalXOfBtn + newX, 0, 70, self.bounds.size.height);
}
} else if (gesture.state == UIGestureRecognizerStateEnded || gesture.state == UIGestureRecognizerStateCancelled) {
CGFloat threshold = -38 + self.contentViewX; // 触发删除按钮的阈值
// 滑动超过一半,固定显示全部按钮
if (self.contentView.frame.origin.x < threshold) {
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
self.contentView.frame = CGRectMake(-56, 0, self.bounds.size.width - 40, self.bounds.size.height);
if (self.isFollowing) {
self.deleteBtn.frame = CGRectMake(self.bgView.bounds.size.width - 70, 0, 70, self.bounds.size.height);
}
} completion:^(BOOL finished) {
self.isOpen = YES;
}];
} else {
// 否则弹回
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
self.contentView.frame = CGRectMake(20, 0, self.bounds.size.width - 40, self.bounds.size.height);
if (self.isFollowing) {
self.deleteBtn.frame = CGRectMake(self.bgView.bounds.size.width + 6, 0, 70, self.bounds.size.height);
}
} completion:^(BOOL finished) {
self.isOpen = NO;
}];
}
}
}
- (void)initView {
self.contentViewX = 20;
self.bgView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width - 20, self.bounds.size.height)];
self.bgView.layer.masksToBounds = YES;
[self addSubview:self.bgView];
self.deleteBtn = [[UIButton alloc] initWithFrame:CGRectMake(self.bgView.bounds.size.width - 70, 0, 70, self.bounds.size.height)];
[self.deleteBtn addTarget:self action:@selector(deleteAction:) forControlEvents:UIControlEventTouchUpInside];
self.deleteBtn.layer.cornerRadius = 15;
self.deleteBtn.layer.masksToBounds = YES;
[self.deleteBtn setTitle:@"删除" forState:UIControlStateNormal];
[self.deleteBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
self.deleteBtn.titleLabel.font = [UIFont systemFontOfSize:20];
self.deleteBtn.backgroundColor = [UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:1.0];;
[self.bgView addSubview:self.deleteBtn];
self.contentView = [[UIView alloc] initWithFrame:CGRectMake(self.contentViewX, 0, self.bgView.bounds.size.width - self.contentViewX, self.bounds.size.height)];
self.contentView.backgroundColor = [UIColor whiteColor];
self.contentView.layer.cornerRadius = 15;
self.contentView.layer.masksToBounds = YES;
[self.bgView addSubview:self.contentView];
self.timeLabel = [[UILabel alloc] initWithFrame:CGRectMake(15, 16, 100, 30)];
self.timeLabel.font = [UIFont systemFontOfSize:20];
self.timeLabel.textColor = [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:1.0];;
[self.contentView addSubview:self.timeLabel];
self.repeatLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 59, 300, 25)];
self.repeatLabel.font = [UIFont systemFontOfSize:16];
self.repeatLabel.textColor = [UIColor colorWithRed:111/255.0 green:111/255.0 blue:111/255.0 alpha:1.0];;
[self.contentView addSubview:self.repeatLabel];
self.switchView = [[UISwitch alloc] initWithFrame:CGRectZero];
self.switchView.center = CGPointMake(self.contentView.bounds.size.width - 45, 30);
self.switchView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
self.switchView.onTintColor = [UIColor colorWithRed:56/255.0 green:255/255.0 blue:209/255.0 alpha:1.0];;
[self.contentView addSubview:self.switchView];
[self.switchView addTarget:self action:@selector(switchAction:) forControlEvents:UIControlEventValueChanged];
self.isFollowing = YES;
}
- (void)deleteAction:(UIButton *)sender {
if (self.deleteBlock) {
self.deleteBlock(self);
}
}
- (void)switchAction:(UISwitch *)sender {
if (self.switchBlock) {
self.switchBlock(self, sender.isOn);
}
}
#pragma mark - reload
- (void)configureWithTime:(NSString *)time repeat:(NSString *)repeat on:(BOOL)on {
self.timeLabel.text = time;
self.repeatLabel.text = repeat;
[self.switchView setOn:on animated:NO];
}
- (void)closeSwipe {
if (!self.isOpen) return;
self.isOpen = NO;
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
self.contentView.frame = CGRectMake(20, 0, self.bounds.size.width - 40, self.bounds.size.height);
if (self.isFollowing) {
self.deleteBtn.frame = CGRectMake(self.bgView.bounds.size.width + 6, 0, 70, self.bounds.size.height);
}
} completion:^(BOOL finished) {}];
}
#pragma mark - lazy load and setters
- (void)setIsFollowing:(BOOL)isFollowing {
_isFollowing = isFollowing;
if (isFollowing) {
self.deleteBtn.frame = CGRectMake(self.bgView.bounds.size.width + 6, 0, 70, self.bounds.size.height);
} else {
//宽度高度缩小,上右下缩小1,因为Core Animation 的"抗锯齿边缘融合"问题(subpixel blending),圆角边缘的半透明像素会"透"出红色
self.deleteBtn.frame = CGRectMake(self.bgView.bounds.size.width - 70, 1, 69, self.bounds.size.height - 2);
}
}
@end
资源更新,新增了添加按钮,详情见链接