iOS—仿tableView自定义闹钟列表

自定义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

资源更新,新增了添加按钮,详情见链接

资源链接:https://github.com/MrZWCui/ClockView.git

相关推荐
liang899940 分钟前
Shiro学习(七):总结Shiro 与Redis 整合过程中的2个问题及解决方案
redis·学习·bootstrap
weixin_486281451 小时前
FFmpeg源码学习---ffmpeg
学习·ffmpeg
画个大饼2 小时前
Objective-C Block 底层原理深度解析
开发语言·ios·objective-c
CodeCipher2 小时前
Java后端程序员学习前端之html
学习·html5
Dr_Zobot3 小时前
SLAM学习系列——ORB-SLAM3安装(Ubuntu20-ROS/Noetic)
学习·ubuntu·软件安装
枫叶20004 小时前
OceanBase数据库-学习笔记5-用户
数据库·笔记·学习·oceanbase
Nuyoah.5 小时前
《Vue3学习手记7》
javascript·vue.js·学习
冰茶_5 小时前
WPF之Button控件详解
大数据·学习·microsoft·c#·wpf
依旧风轻5 小时前
详解 Network.framework:iOS 网络开发的新基石
ios·network·sqi·nw