当前位置: 首页>前端>正文

IOS框架使用:常见框架

原创:知识点总结性文章
创作不易,请珍惜,之后会持续更新,不断完善
个人比较喜欢做笔记和写总结,毕竟好记性不如烂笔头哈哈,这些文章记录了我的IOS成长历程,希望能与大家一起进步
温馨提示:由于简书不支持目录跳转,大家可通过command + F 输入目录标题后迅速寻找到你所需要的内容

目录

  • 一、YYModel
  • 二、UICountingLabel
  • 三、SDWebImage
  • 四、SDCycleScrollView
  • 五、MBProgressHUD
  • 六、EmptyDataSet
  • 七、MGSwipeTableCell
  • 八、FSCalendar
  • 九、Toast
  • 十、zhPopupController

一、YYModel

在iOS开发中总会用到各种JSON与模型相互转换的需求,之前的项目中一直使用MJExtension,但是最近发现一个轻量级的库YYModel,使用简单,性能也很不错,接下来就说说YYModel的一些简单的用法及注意事项。

1、JSON转换为模型,或者模型转换为JSON

JSON
{
    accountId = "<null>";
    avatarUrl = "http://thirdwx.qlogo.cn/mmopen/vi_32/sqicFCgiaheqCFbjrYgTmwXUQOB5l5Iyo47cVVp2cLlHARck6XgXscqicJ2ZVibicCbGH4iaVcQz8ptXqbV6n9ic5iaL9g/132";
    unionId = o2t87wKAR1pNRZR0cnReBT608mok;
    userid = 11703346;
    wxNickName = "\U90a3\U5496";
}
Model
@property (nonatomic, strong) NSString *avatarUrl;
@property (nonatomic, strong) NSString *wxNickName;
@property (nonatomic, strong) NSString *accountId;
@property (nonatomic, strong) NSString *unionId;
@property (nonatomic, strong) NSString *userid;
JSON转换为模型
FansModel *fansModel = [FansModel yy_modelWithJSON:dic];
FansModel *fansModel = [FansModel yy_modelWithDictionary:dic];
模型转换为JSON
NSDictionary *json = [fansModel yy_modelToJSONObject];

2、手动创建的模型和服务端返回的模型属性名称不一致时

"userName" = "谢佳培";
"avatar" = "http://thirdwx.qlogo.cn/mmopen/vi_32/sqicFCgiaheqCFbjrYgTmwXUQOB5l5Iyo47cVVp2cLlHARck6XgXscqicJ2ZVibicCbGH4iaVcQz8ptXqbV6n9ic5iaL9g/132";

@property (nonatomic, strong) NSString *avatarUrl;
@property (nonatomic, strong) NSString *accountId;
+ (NSDictionary<NSString *, id> *)modelCustomPropertyMapper
{
    return @{@"avatarUrl" : @"avatar",
             @"wxNickName" : @"userName",
             };
}

3、模型套模型

+ (NSDictionary *)modelContainerPropertyGenericClass
{
    return @{@"areas" : [AreaModel class]};
}
ProvinceModel *provinceModel = [ProvinceModel yy_modelWithJSON:dict];
CityModel *cityModel = provinceModel.citys[0];

4、数组里面存放字典类型转换为模型

@implementation RabbitModel

@end

@implementation RabbitViewModel

// 转换为模型
- (void)manyRabbit
{
    NSArray *array;
    NSArray<RabbitModel *> *rabbitModelArray = [NSArray yy_modelArrayWithClass:[RabbitModel class] json:array];
}

@end

5、黑名单,即加入黑名单的字段会被忽略不会转换

JSON转换为模型,转换出来的模型中的avatarUrlaccountId的值为nil

@property (nonatomic, strong) NSString *avatarUrl;
@property (nonatomic, strong) NSString *accountId;

+ (NSArray *)modelPropertyBlacklist
{
    return @[@"avatarUrl", @"accountId"];
}

6、白名单,即只处理加入白名单的字段,其余字段忽略掉

+ (NSArray *)modelPropertyWhitelist
{
    return @[@"avatarUrl", @"accountId"];
}

二、UICountingLabel

IOS框架使用:常见框架,第1张
UICountingLabel

1、做一个算数的

myLabel.method = UILabelCountingMethodLinear;// 线性变化
myLabel.format = @"%d";
[myLabel countFrom:1 to:10 withDuration:3.0];

2、使用ease-in-out(默认设置)将计数从5%增加到10%

countPercentageLabel.format = @"%.1f%%";
[countPercentageLabel countFrom:5 to:10];

3、使用使用数字格式化的字符串进行计数

NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
formatter.numberStyle = kCFNumberFormatterDecimalStyle;

// 可以对显示的文本格式进行自定义
scoreLabel.formatBlock = ^NSString *(CGFloat value)
{
    NSString *formatted = [formatter stringFromNumber:@((int)value)];
    return [NSString stringWithFormat:@"Score: %@",formatted];
};
scoreLabel.method = UILabelCountingMethodEaseOut;// 开始速度很快,快结束时变得缓慢
[scoreLabel countFrom:0 to:10000 withDuration:2.5];// 可以指定动画的时长,默认时长是2.0秒

4、使用属性字符串计数

NSInteger toValue = 100;

// 用于设置属性字符串的格式,如果指定了formatBlock,则将会覆盖掉format属性,因为block的优先级更高
attributedLabel.attributedFormatBlock = ^NSAttributedString* (CGFloat value)
{
    NSDictionary *normal = @{ NSFontAttributeName: [UIFont fontWithName: @"HelveticaNeue-UltraLight" size: 20] };
    NSDictionary *highlight = @{ NSFontAttributeName: [UIFont fontWithName: @"HelveticaNeue" size: 20] };
    
    NSString *prefix = [NSString stringWithFormat:@"%d", (int)value];
    NSString *postfix = [NSString stringWithFormat:@"/%d", (int)toValue];
    
    NSMutableAttributedString *prefixAttr = [[NSMutableAttributedString alloc] initWithString: prefix attributes: highlight];
    NSAttributedString *postfixAttr = [[NSAttributedString alloc] initWithString: postfix attributes: normal];
    [prefixAttr appendAttributedString: postfixAttr];
    
    return prefixAttr;
};
[attributedLabel countFrom:0 to:toValue withDuration:2.5];

5、获得动画结束的事件

self.completionBlockLabel.method = UILabelCountingMethodEaseInOut;
self.completionBlockLabel.format = @"%d%%";

__weak UICountingLabelViewController *weakSelf = self;
self.completionBlockLabel.completionBlock = ^{
    weakSelf.completionBlockLabel.textColor = [UIColor colorWithRed:0 green:0.5 blue:0 alpha:1];
};
[self.completionBlockLabel countFrom:0 to:100];

三、SDWebImage

多数情况下是使用UIImageView+WebCacheUIImageView异步加载图片:

#import <SDWebImage/UIImageView+WebCache.h>

[cell.imageView setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"] placeholderImage:[UIImage imageNamed:@"placeholder.png"]];

它还支持block语法,用于在加载完成时做一些操作:

[cell.imageView setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"] placeholderImage:[UIImage imageNamed:@"placeholder.png"] completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) {
    ... completion code here ...
}];

SDWebImage并不局限于UIImageView,使用SDWebImageManager可完成更多的操作:

SDWebImageManager *manager = [SDWebImageManager sharedManager];
[manager downloadWithURL:imageURL options:0 progress:^(NSUInteger receivedSize, long long expectedSize) {
    // 下载进度
} completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) {
    if (image)
    {
        // 下载完成
    }
}];

或者使用Image Downloader也是一样的效果:

[SDWebImageDownloader.sharedDownloader downloadImageWithURL:imageURL options:0 progress:^(NSUInteger receivedSize, long long expectedSize) {
    // 进度
} completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
    if (image && finished)
    {
        // 下载完成
    }
}];

四、SDCycleScrollView

IOS框架使用:常见框架,第2张
SDCycleScrollView

1、带标题的网络图片

SDCycleScrollView *cycleScrollView = [SDCycleScrollView cycleScrollViewWithFrame:CGRectMake(0, 100, SCREEN_WIDTH, 200) delegate:self placeholderImage:[UIImage imageNamed:@"placeholder"]];
// page位置,默认居中
cycleScrollView.pageControlAliment = SDCycleScrollViewPageContolAlimentRight;
// 自定义分页控件小圆标颜色
cycleScrollView.currentPageDotColor = [UIColor whiteColor];
// 添加标题
cycleScrollView.titlesGroup = _titles;
[_ContainerView addSubview:cycleScrollView];

// 模拟加载延迟
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    cycleScrollView.imageURLStringsGroup = self->_imagesURLStrings;
});

// block监听点击方式
cycleScrollView.clickItemOperationBlock = ^(NSInteger currentIndex) {
    NSLog(@"block监听点击方式,位置为:%ld", (long)currentIndex);
};

2、自定义分页控件

cycleScrollView.currentPageDotImage = [UIImage imageNamed:@"pageControlCurrentDot"];
cycleScrollView.pageDotImage = [UIImage imageNamed:@"pageControlDot"];

3、本地图片

SDCycleScrollView *cycleScrollView = [SDCycleScrollView cycleScrollViewWithFrame:CGRectMake(0, 650, SCREEN_WIDTH, 300) shouldInfiniteLoop:YES imageNamesGroup:_imageNames];
cycleScrollView.scrollDirection = UICollectionViewScrollDirectionVertical;

// 仅显示文字
NSMutableArray *titlesArray = [NSMutableArray new];
[titlesArray addObject:@"纯文字"];
[titlesArray addObjectsFromArray:_titles];
cycleScrollView.titlesGroup = [titlesArray copy];
[cycleScrollView disableScrollGesture];

[_ContainerView addSubview:cycleScrollView];

4、自定义cell的轮播图

a、如果要实现自定义cell的轮播图,必须先实现代理方法
_customCellScrollView = [SDCycleScrollView cycleScrollViewWithFrame:CGRectMake(0, 1000, SCREEN_WIDTH, 200) delegate:self placeholderImage:[UIImage imageNamed:@"placeholder"]];
_customCellScrollView.imageURLStringsGroup = _imagesURLStrings;
[_ContainerView addSubview:_customCellScrollView];
b、点击图片回调
- (void)cycleScrollView:(SDCycleScrollView *)cycleScrollView didSelectItemAtIndex:(NSInteger)index
{
    NSLog(@"点击了第%ld张图片", (long)index);
    [self.navigationController pushViewController:[NSClassFromString(@"DemoVC") new] animated:YES];
}
c、滚动到第几张图回调
- (void)cycleScrollView:(SDCycleScrollView *)cycleScrollView didScrollToIndex:(NSInteger)index
{
    NSLog(@"滚动到第%ld张图", (long)index);
}

五、MBProgressHUD

IOS框架使用:常见框架,第3张
MBProgress

实现MBProgressHUDDelegate协议,并声明HUD变量:

@interface ViewController () <MBProgressHUDDelegate>
{
    MBProgressHUD *HUD;
}
#pragma mark - MBProgressHUDDelegate

- (void)hudWasHidden:(MBProgressHUD *)hud
{
    [HUD removeFromSuperview];
    HUD = nil;
}

在执行某个异步请求时开始调用:

HUD = [MBProgressHUD showHUDAddedTo:self.webView animated:YES];
HUD.labelText = @"正在请求...";
HUD.mode = MBProgressHUDModeText;// mode参数可以控制显示的模式
HUD.delegate = self;

请求完成时隐藏提示效果:

[HUD hide:YES];

对于同步方法一般都是用showWhileExecuting方法,方法执行完成之后会自动隐藏:

[HUD showWhileExecuting:@selector(myTask) onTarget:self withObject:nil animated:YES];
配置进度条的属性
// 只能设置菊花的颜色 全局设置
[UIActivityIndicatorView appearanceWhenContainedInInstancesOfClasses:@[[MBProgressHUD class]]].color = [UIColor redColor];

// 提示框格式 Determinate-圆形饼图 HorizontalBar- 水平进度条
// AnnularDeterminate-圆环 CustomView-自定义视图 Text-文本
self.progressHUD.mode = MBProgressHUDModeIndeterminate;

// 设置进度框中的提示文字
self.progressHUD.label.text = @"加载中...";
// 信息详情
_progressHUD.detailsLabel.text = @"人善如菊,夜凉如水...";
// 进度指示器  模式是0,取值从0.0————1.0
_progressHUD.progress = 0.5;

// 设置背景框的透明度,默认0.8
_progressHUD.bezelView.opaque = 1;
// 设置背景颜色之后opacity属性的设置将会失效
_progressHUD.bezelView.color = [[UIColor redColor] colorWithAlphaComponent:1];
// 背景框的圆角值,默认是10
_progressHUD.bezelView.layer.cornerRadius = 20;

// 置提示框的相对于父视图中心点位置,正值 向右下偏移,负值左上
[_progressHUD setOffset:CGPointMake(-80, -100)];
// 设置各个元素距离矩形边框的距离
[_progressHUD setMargin:5];
// 背景框的最小尺寸
_progressHUD.minSize = CGSizeMake(50, 50);
// 是否强制背景框宽高相等
_progressHUD.square = YES;

// ZoomOut:逐渐的后退消失 ZoomIn:前进淡化消失
// 默认类型的,渐变
_progressHUD.animationType = MBProgressHUDAnimationFade;
// 设置最短显示时间,为了避免显示后立刻被隐藏   默认是0
_progressHUD.minShowTime = 5;

// 被调用方法在宽限期内执行完,则HUD不会被显示
// 为了避免在执行很短的任务时,去显示一个HUD窗口
// _progressHUD.graceTime

// 设置隐藏的时候是否从父视图中移除,默认是NO
_progressHUD.removeFromSuperViewOnHide = NO;

// 显示进度框
[self.progressHUD showAnimated:YES];

// 两种隐藏的方法
// [_progressHUD hideAnimated:YES];
[_progressHUD hideAnimated:YES afterDelay:5];

// 隐藏时候的回调 隐藏动画结束之后
_progressHUD.completionBlock = ^{
    NSLog(@"哈哈哈哈哈哈哈哈哈");
};

六、EmptyDataSet

IOS框架使用:常见框架,第4张
EmptyDataSet
#import <UIScrollView+EmptyDataSet.h>

@interface EmptyDetailViewController ()<DZNEmptyDataSetSource, DZNEmptyDataSetDelegate>

self.tableView.emptyDataSetSource = self;
self.tableView.emptyDataSetDelegate = self;

1、DZNEmptyDataSetSource

根据APP类型来设置空图的带属性标题
- (NSAttributedString *)titleForEmptyDataSet:(UIScrollView *)scrollView
{
    NSString *text = nil;
    UIFont *font = nil;
    UIColor *textColor = nil;
    NSMutableDictionary *attributes = [NSMutableDictionary new];
    
    switch (self.application.type)
    {
        case ApplicationType500px:
        {
            text = @"No Photos";
            font = [UIFont boldSystemFontOfSize:17.0];
            textColor = [UIColor colorWithHex:@"545454"];
            break;
        }
......
    if (!text)
    {
        return nil;
    }
    
    if (font)
    {
        [attributes setObject:font forKey:NSFontAttributeName];
    }
    
    if (textColor)
    {
        [attributes setObject:textColor forKey:NSForegroundColorAttributeName];
    }
    
    return [[NSAttributedString alloc] initWithString:text attributes:attributes];
}
根据APP类型来设置空图的带属性描述
- (NSAttributedString *)descriptionForEmptyDataSet:(UIScrollView *)scrollView
{
    NSString *text = nil;
    UIFont *font = nil;
    UIColor *textColor = nil;
    
    NSMutableDictionary *attributes = [NSMutableDictionary new];
    NSMutableParagraphStyle *paragraph = [NSMutableParagraphStyle new];
    paragraph.lineBreakMode = NSLineBreakByWordWrapping;
    paragraph.alignment = NSTextAlignmentCenter;
    
    switch (self.application.type)
    {
        case ApplicationType500px:
        {
            text = @"Get started by uploading a photo.";
            font = [UIFont boldSystemFontOfSize:15.0];
            textColor = [UIColor colorWithHex:@"545454"];
            break;
        }
......
    if (!text)
    {
        return nil;
    }
    
    if (font) [attributes setObject:font forKey:NSFontAttributeName];
    if (textColor) [attributes setObject:textColor forKey:NSForegroundColorAttributeName];
    if (paragraph) [attributes setObject:paragraph forKey:NSParagraphStyleAttributeName];

    NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:text attributes:attributes];
    
    switch (self.application.type)
    {
        case ApplicationTypeSkype:
            [attributedString addAttribute:NSForegroundColorAttributeName value:[UIColor colorWithHex:@"00adf1"] range:[attributedString.string rangeOfString:@"add favorites"]];
            break;
        default:
            break;
    }
    
    return attributedString;
}
获取到占位图
- (UIImage *)imageForEmptyDataSet:(UIScrollView *)scrollView
{
    if (self.isLoading)
    {
        return [UIImage imageNamed:@"loading_imgBlue_78x78" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
    }
    else
    {
        NSString *imageName = [[[NSString stringWithFormat:@"placeholder_%@", self.application.displayName] lowercaseString] stringByReplacingOccurrencesOfString:@" " withString:@"_"];
        
        UIImage *image = [UIImage imageNamed:imageName inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
        
        return image;
    }
}
生成占位动画
- (CAAnimation *)imageAnimationForEmptyDataSet:(UIScrollView *)scrollView
{
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform"];
    animation.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
    animation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI_2, 0.0, 0.0, 0.0)];
    animation.duration = 0.25;
    animation.cumulative = YES;
    animation.repeatCount = MAXFLOAT;
    
    return animation;
}
根据APP类型配置空图的按钮属性文本
- (NSAttributedString *)buttonTitleForEmptyDataSet:(UIScrollView *)scrollView forState:(UIControlState)state
{
    NSString *text = nil;
    UIFont *font = nil;
    UIColor *textColor = nil;
    
    switch (self.application.type)
    {
        case ApplicationTypeAirbnb:
        {
            text = @"Start Browsing";
            font = [UIFont boldSystemFontOfSize:16.0];
            textColor = [UIColor colorWithHex:(state == UIControlStateNormal) @"05adff" : @"6bceff"];
            break;
        }
.....
    if (!text)
    {
        return nil;
    }
    
    NSMutableDictionary *attributes = [NSMutableDictionary new];
    if (font) [attributes setObject:font forKey:NSFontAttributeName];
    if (textColor) [attributes setObject:textColor forKey:NSForegroundColorAttributeName];
    
    return [[NSAttributedString alloc] initWithString:text attributes:attributes];
}
根据APP类型配置空图的按钮属性文本
- (UIImage *)buttonBackgroundImageForEmptyDataSet:(UIScrollView *)scrollView forState:(UIControlState)state
{
    NSString *imageName = [[NSString stringWithFormat:@"button_background_%@", self.application.displayName] lowercaseString];
    UIImage *image = [UIImage imageNamed:imageName inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
    if (state == UIControlStateNormal) imageName = [imageName stringByAppendingString:@"_normal"];
    if (state == UIControlStateHighlighted) imageName = [imageName stringByAppendingString:@"_highlight"];
    
    // Insets
    UIEdgeInsets capInsets = UIEdgeInsetsMake(10.0, 10.0, 10.0, 10.0);
    UIEdgeInsets rectInsets = UIEdgeInsetsZero;
    switch (self.application.type)
    {
        case ApplicationTypeFoursquare:
            capInsets = UIEdgeInsetsMake(25.0, 25.0, 25.0, 25.0);
            rectInsets = UIEdgeInsetsMake(0.0, 10, 0.0, 10);
            break;
.....
    return [[image resizableImageWithCapInsets:capInsets resizingMode:UIImageResizingModeStretch] imageWithAlignmentRectInsets:rectInsets];
}
背景颜色
- (UIColor *)backgroundColorForEmptyDataSet:(UIScrollView *)scrollView
{
    switch (self.application.type)
    {
        case ApplicationType500px:      return [UIColor blackColor];
        case ApplicationTypeAirbnb:     return [UIColor whiteColor];
.....
}
垂直偏移量
- (CGFloat)verticalOffsetForEmptyDataSet:(UIScrollView *)scrollView
{
    if (self.application.type == ApplicationTypeKickstarter)
    {
        CGFloat offset = CGRectGetHeight([UIApplication sharedApplication].statusBarFrame);
        offset += CGRectGetHeight(self.navigationController.navigationBar.frame);
        return -offset;
    }
    if (self.application.type == ApplicationTypeTwitter)
    {
        return -roundf(self.tableView.frame.size.height/2.5);
    }
    return 0.0;
}
空白高度
- (CGFloat)spaceHeightForEmptyDataSet:(UIScrollView *)scrollView
{
    switch (self.application.type)
    {
        case ApplicationType500px:          return 9.0;
        case ApplicationTypeAirbnb:         return 24.0;
......
}

2、DZNEmptyDataSetDelegate

允许展示
- (BOOL)emptyDataSetShouldDisplay:(UIScrollView *)scrollView
{
    return YES;
}
允许触摸
- (BOOL)emptyDataSetShouldAllowTouch:(UIScrollView *)scrollView
{
    return YES;
}
允许滚动
- (BOOL)emptyDataSetShouldAllowScroll:(UIScrollView *)scrollView
{
    return YES;
}
是否允许展示加载动画
- (BOOL)emptyDataSetShouldAnimateImageView:(UIScrollView *)scrollView
{
    return self.isLoading;
}
点击View的事件
- (void)emptyDataSet:(UIScrollView *)scrollView didTapView:(UIView *)view
{
    self.loading = YES;
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        self.loading = NO;
    });
}
点击Button的事件
- (void)emptyDataSet:(UIScrollView *)scrollView didTapButton:(UIButton *)button
{
    self.loading = YES;
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        self.loading = NO;
    });
}

七、MGSwipeTableCell

IOS框架使用:常见框架,第5张
MGSwipeTableCell
#import <MGSwipeTableCell.h>
@interface MailViewController ()<MGSwipeTableCellDelegate>

1、MGSwipeTableCellDelegate

配置按钮
- (NSArray *)swipeTableCell:(MGSwipeTableCell*)cell swipeButtonsForDirection:(MGSwipeDirection)direction swipeSettings:(MGSwipeSettings*)swipeSettings expansionSettings:(MGSwipeExpansionSettings *)expansionSettings
{
    swipeSettings.transition = MGSwipeTransitionBorder;
    expansionSettings.buttonIndex = 0;

    __weak MailViewController *weakSelf = self;
    MailData *mail = [weakSelf mailForIndexPath:[self.tableView indexPathForCell:cell]];
    
    if (direction == MGSwipeDirectionLeftToRight)// 从左向右滑{}
    else{}// 从右向左滑
    return nil;
    
}
从左向右滑
expansionSettings.fillOnTrigger = NO;
expansionSettings.threshold = 2;

return @[[MGSwipeButton buttonWithTitle:[weakSelf readButtonText:mail.read] backgroundColor:[UIColor colorWithRed:0 green:122/255.0 blue:1.0 alpha:1.0] padding:5 callback:^BOOL(MGSwipeTableCell *sender) {
    
    MailData *mail = [weakSelf mailForIndexPath:[weakSelf.tableView indexPathForCell:sender]];
    mail.read = !mail.read;
    
    // 刷新单元格内容时需要刷新
    [weakSelf updateCellIndicactor:mail cell:(MailTableCell*)sender];
    [cell refreshContentView];
    [(UIButton *)[cell.leftButtons objectAtIndex:0] setTitle:[weakSelf readButtonText:mail.read] forState:UIControlStateNormal];
    
    return YES;
}]];
从右向左滑
expansionSettings.fillOnTrigger = YES;
expansionSettings.threshold = 1.1;
CGFloat padding = 15;
MGSwipeButton *trash = [MGSwipeButton buttonWithTitle:@"Trash" backgroundColor:[UIColor colorWithRed:1.0 green:59/255.0 blue:50/255.0 alpha:1.0] padding:padding callback:^BOOL(MGSwipeTableCell *sender) {
    
    NSIndexPath *indexPath = [weakSelf.tableView indexPathForCell:sender];
    [weakSelf deleteMail:indexPath];
    
    // 不自动隐藏以改进删除动画
    return NO;
}];
MGSwipeButton *flag = [MGSwipeButton buttonWithTitle:@"Flag" backgroundColor:[UIColor colorWithRed:1.0 green:149/255.0 blue:0.05 alpha:1.0] padding:padding callback:^BOOL(MGSwipeTableCell *sender) {
    
    MailData *mail = [weakSelf mailForIndexPath:[weakSelf.tableView indexPathForCell:sender]];
    mail.flag = !mail.flag;
    
    // 刷新单元格内容时需要刷新
    [weakSelf updateCellIndicactor:mail cell:(MailTableCell*)sender];
    [cell refreshContentView];
    
    return YES;
}];
MGSwipeButton *more = [MGSwipeButton buttonWithTitle:@"More" backgroundColor:[UIColor colorWithRed:200/255.0 green:200/255.0 blue:205/255.0 alpha:1.0] padding:padding callback:^BOOL(MGSwipeTableCell *sender) {
    
    return NO;
}];

return @[trash, flag, more];
监听滑动方向和按钮状态
- (void)swipeTableCell:(MGSwipeTableCell*) cell didChangeSwipeState:(MGSwipeState)state gestureIsActive:(BOOL)gestureIsActive
{
    NSString *string;
    switch (state)
    {
        case MGSwipeStateNone: string = @"None"; break;
        case MGSwipeStateSwippingLeftToRight: string = @"SwippingLeftToRight"; break;
        case MGSwipeStateSwippingRightToLeft: string = @"SwippingRightToLeft"; break;
        case MGSwipeStateExpandingLeftToRight: string = @"ExpandingLeftToRight"; break;
        case MGSwipeStateExpandingRightToLeft: string = @"ExpandingRightToLeft"; break;
    }
    NSLog(@"Swipe state: %@ ::: Gesture: %@", string, gestureIsActive @"Active" : @"Ended");
}
辅助方法
- (NSString *)readButtonText:(BOOL) read
{
    return read @"Mark as\nunread" :@"Mark as\nread";
}

- (MailData *)mailForIndexPath:(NSIndexPath*) path
{
    return [self.demoData objectAtIndex:path.row];
}

- (void)deleteMail:(NSIndexPath *)indexPath
{
    [self.demoData removeObjectAtIndex:indexPath.row];
    [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationLeft];
}
更新指示器的颜色
- (void)updateCellIndicactor:(MailData *)mail cell:(MailTableCell *)cell
{
    UIColor *color;
    UIColor *innerColor;
    
    if (!mail.read && mail.flag)
    {
        color = [UIColor colorWithRed:1.0 green:149/255.0 blue:0.05 alpha:1.0];
        innerColor = [UIColor colorWithRed:0 green:122/255.0 blue:1.0 alpha:1.0];
    }
    else if (mail.flag)
    {
        color = [UIColor colorWithRed:1.0 green:149/255.0 blue:0.05 alpha:1.0];
    }
    else if (mail.read)
    {
        color = [UIColor clearColor];
    }
    else
    {
        color = [UIColor colorWithRed:0 green:122/255.0 blue:1.0 alpha:1.0];
    }
    
    cell.indicatorView.indicatorColor = color;
    cell.indicatorView.innerColor = innerColor;
}

八、FSCalendar

1、Range Picker

IOS框架使用:常见框架,第6张
Range Picker
导入头文件
#import <FSCalendar/FSCalendar.h>

@interface RangePickerCell : FSCalendarCell

// 范围的开始/结束
@property (weak, nonatomic) CALayer *selectionLayer;
// 范围的中间
@property (weak, nonatomic) CALayer *middleLayer;

@end

@interface RangePickerViewController ()<FSCalendarDataSource,FSCalendarDelegate,FSCalendarDelegateAppearance>

@end
创建日历视图
- (void)createSubview
{
    // 初始化属性
    self.gregorian = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
    self.dateFormatter = [[NSDateFormatter alloc] init];
    self.dateFormatter.dateFormat = @"yyyy-MM-dd";

    // 创建calendar
    FSCalendar *calendar = [[FSCalendar alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(self.navigationController.navigationBar.frame), self.view.frame.size.width, self.view.frame.size.height - CGRectGetMaxY(self.navigationController.navigationBar.frame))];
    calendar.dataSource = self;
    calendar.delegate = self;
    calendar.pagingEnabled = NO;
    calendar.allowsMultipleSelection = YES;
    calendar.rowHeight = 60;
    calendar.placeholderType = FSCalendarPlaceholderTypeNone;
    [self.view addSubview:calendar];
    self.calendar = calendar;
    
    calendar.appearance.titleDefaultColor = [UIColor blackColor];
    calendar.appearance.headerTitleColor = [UIColor blackColor];
    calendar.appearance.titleFont = [UIFont systemFontOfSize:16];
    
    calendar.scope = FSCalendarScopeWeek;
    calendar.weekdayHeight = 20;

    calendar.swipeToChooseGesture.enabled = YES;
    
    // Hide the today circle
    // calendar.today = nil;
    [calendar registerClass:[RangePickerCell class] forCellReuseIdentifier:@"RangePickerCell"];
}
FSCalendarDataSource
最小日期
- (NSDate *)minimumDateForCalendar:(FSCalendar *)calendar
{
    return [self.dateFormatter dateFromString:@"2020-06-08"];
}
最大日期
- (NSDate *)maximumDateForCalendar:(FSCalendar *)calendar
{
    return [self.gregorian dateByAddingUnit:NSCalendarUnitMonth value:10 toDate:[NSDate date] options:0];
}
今日的标题
- (NSString *)calendar:(FSCalendar *)calendar titleForDate:(NSDate *)date
{
    if ([self.gregorian isDateInToday:date])
    {
        return @"今";
    }
    return nil;
}
创建Cell
- (FSCalendarCell *)calendar:(FSCalendar *)calendar cellForDate:(NSDate *)date atMonthPosition:(FSCalendarMonthPosition)monthPosition
{
    RangePickerCell *cell = [calendar dequeueReusableCellWithIdentifier:@"RangePickerCell" forDate:date atMonthPosition:monthPosition];
    return cell;
}
配置Cell
- (void)calendar:(FSCalendar *)calendar willDisplayCell:(FSCalendarCell *)cell forDate:(NSDate *)date atMonthPosition: (FSCalendarMonthPosition)monthPosition
{
    [self configureCell:cell forDate:date atMonthPosition:monthPosition];
}
FSCalendarDelegate
是否可以选中日期
- (BOOL)calendar:(FSCalendar *)calendar shouldSelectDate:(NSDate *)date atMonthPosition:(FSCalendarMonthPosition)monthPosition
{
    return monthPosition == FSCalendarMonthPositionCurrent;
}
是否支持反选日期
- (BOOL)calendar:(FSCalendar *)calendar shouldDeselectDate:(NSDate *)date atMonthPosition:(FSCalendarMonthPosition)monthPosition
{
    return NO;
}
选中日期
- (void)calendar:(FSCalendar *)calendar didSelectDate:(NSDate *)date atMonthPosition:(FSCalendarMonthPosition)monthPosition
{
    // 如果选择是由滑动手势引起的
    if (calendar.swipeToChooseGesture.state == UIGestureRecognizerStateChanged)
    {
        
        if (!self.startDate)
        {
            self.startDate = date;
        }
        else
        {
            if (self.endDate)
            {
                [calendar deselectDate:self.endDate];
            }
            self.endDate = date;
        }
    }
    else
    {
        if (self.endDate)
        {
            [calendar deselectDate:self.startDate];
            [calendar deselectDate:self.endDate];
            self.startDate = date;
            self.endDate = nil;
        }
        else if (!self.startDate)
        {
            self.startDate = date;
        }
        else
        {
            self.endDate = date;
        }
    }
    
    [self configureVisibleCells];
}
取消选中
- (void)calendar:(FSCalendar *)calendar didDeselectDate:(NSDate *)date atMonthPosition:(FSCalendarMonthPosition)monthPosition
{
    NSLog(@"取消选中的日期为:%@",[self.dateFormatter stringFromDate:date]);
    [self configureVisibleCells];
}
日期事件颜色
- (NSArray<UIColor *> *)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance eventDefaultColorsForDate:(NSDate *)date
{
    if ([self.gregorian isDateInToday:date])
    {
        return @[[UIColor orangeColor]];
    }
    return @[appearance.eventDefaultColor];
}
辅助方法
配置可见日期的选中状态
- (void)configureVisibleCells
{
    [self.calendar.visibleCells enumerateObjectsUsingBlock:^(__kindof FSCalendarCell * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        NSDate *date = [self.calendar dateForCell:obj];
        FSCalendarMonthPosition position = [self.calendar monthPositionForCell:obj];
        [self configureCell:obj forDate:date atMonthPosition:position];
    }];
}
配置每一个日期的选中状态
- (void)configureCell:(__kindof FSCalendarCell *)cell forDate:(NSDate *)date atMonthPosition:(FSCalendarMonthPosition)position
{
    RangePickerCell *rangeCell = cell;
    if (position != FSCalendarMonthPositionCurrent)
    {
        rangeCell.middleLayer.hidden = YES;
        rangeCell.selectionLayer.hidden = YES;
        return;
    }
    if (self.startDate && self.endDate)
    {
        BOOL isMiddle = [date compare:self.startDate] != [date compare:self.endDate];
        rangeCell.middleLayer.hidden = !isMiddle;
    }
    else
    {
        rangeCell.middleLayer.hidden = YES;
    }
    BOOL isSelected = NO;
    isSelected |= self.startDate && [self.gregorian isDate:date inSameDayAsDate:self.startDate];
    isSelected |= self.endDate && [self.gregorian isDate:date inSameDayAsDate:self.endDate];
    rangeCell.selectionLayer.hidden = !isSelected;
}

2、DIY

IOS框架使用:常见框架,第7张
DIY

选中的是17号,但是打印结果显示选中的是16日,默认选中的3天也各自少了一天,说明这个框架有Bug呀。

2020-11-05 11:39:11.250332+0800 UseUIControlFramework[22930:4990809] did select date: 2020-11-17
2020-11-05 11:39:17.708906+0800 UseUIControlFramework[22930:4990809] 不是当前处理的日期月份页面(比如下一个月)则直接隐藏圆圈和选中图层
2020-11-05 11:39:23.939118+0800 UseUIControlFramework[22930:4990809] 选中的日期包括:(
    "2020-11-03 16:00:00 +0000",
    "2020-11-04 16:00:00 +0000",
    "2020-11-05 16:00:00 +0000",
    "2020-11-16 16:00:00 +0000"
)
2020-11-05 11:40:15.690340+0800 UseUIControlFramework[22930:4990809] 选中则显示并更新选中图层
2020-11-05 13:37:58.433376+0800 UseUIControlFramework[22930:4990809] 未选中
2020-11-05 13:38:02.752246+0800 UseUIControlFramework[22930:4990809] 未选中则隐藏选中图层
创建日历视图
- (void)createSubview
{
    UIView *view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    view.backgroundColor = [UIColor systemGroupedBackgroundColor];
    self.view = view;
    
    CGFloat height = [[UIDevice currentDevice].model hasPrefix:@"iPad"] 450 : 300;
    FSCalendar *calendar = [[FSCalendar alloc] initWithFrame:CGRectMake(0,  CGRectGetMaxY(self.navigationController.navigationBar.frame) + 44, view.frame.size.width, height)];
    calendar.dataSource = self;
    calendar.delegate = self;
    calendar.swipeToChooseGesture.enabled = YES;// 支持滑动选中手势
    calendar.allowsMultipleSelection = YES;// 支持多选
    [view addSubview:calendar];
    self.calendar = calendar;
    
    // 头部视图
    calendar.calendarHeaderView.backgroundColor = [[UIColor lightGrayColor] colorWithAlphaComponent:0.1];
    // 周视图
    calendar.calendarWeekdayView.backgroundColor = [[UIColor lightGrayColor] colorWithAlphaComponent:0.1];
    // 事件选中的颜色
    calendar.appearance.eventSelectionColor = [UIColor whiteColor];
    // 事件偏移量
    calendar.appearance.eventOffset = CGPointMake(0, -7);
    // 隐藏今天的圆圈
    calendar.today = nil;
    // 注册Cell
    [calendar registerClass:[DIYCalendarCell class] forCellReuseIdentifier:@"cell"];
    
    // 扇动的手势
    UIPanGestureRecognizer *scopeGesture = [[UIPanGestureRecognizer alloc] initWithTarget:calendar action:@selector(handleScopeGesture:)];
    [calendar addGestureRecognizer:scopeGesture];
    
    // 创建事件Label
    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(calendar.frame)+10, self.view.frame.size.width, 50)];
    label.textAlignment = NSTextAlignmentCenter;
    label.font = [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline];
    [self.view addSubview:label];
    self.eventLabel = label;
    
    // 配置事件Label的文本属性
    NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:@""];
    NSTextAttachment *attatchment = [[NSTextAttachment alloc] init];
    attatchment.image = [UIImage imageNamed:@"icon_cat"];
    attatchment.bounds = CGRectMake(0, -3, attatchment.image.size.width, attatchment.image.size.height);
    [attributedText appendAttributedString:[NSAttributedString attributedStringWithAttachment:attatchment]];
    [attributedText appendAttributedString:[[NSAttributedString alloc] initWithString:@"  Hey Daily Event  "]];
    [attributedText appendAttributedString:[NSAttributedString attributedStringWithAttachment:attatchment]];
    self.eventLabel.attributedText = attributedText.copy;
}
FSCalendarDataSource
事件数量
- (NSInteger)calendar:(FSCalendar *)calendar numberOfEventsForDate:(NSDate *)date
{
    return 2;
}
FSCalendarDelegate
调整事件Label的位置
- (void)calendar:(FSCalendar *)calendar boundingRectWillChange:(CGRect)bounds animated:(BOOL)animated
{
    calendar.frame = (CGRect){calendar.frame.origin,bounds.size};
    
    self.eventLabel.frame = CGRectMake(0, CGRectGetMaxY(calendar.frame)+10, self.view.frame.size.width, 50);
    
}
是否支持反选
- (BOOL)calendar:(FSCalendar *)calendar shouldDeselectDate:(NSDate *)date atMonthPosition:(FSCalendarMonthPosition)monthPosition
{
    return monthPosition == FSCalendarMonthPositionCurrent;
}
选中日期
- (void)calendar:(FSCalendar *)calendar didSelectDate:(NSDate *)date atMonthPosition:(FSCalendarMonthPosition)monthPosition
{
    NSLog(@"did select date: %@",[self.dateFormatter stringFromDate:date]);
    [self configureVisibleCells];
}
反选日期
- (void)calendar:(FSCalendar *)calendar didDeselectDate:(NSDate *)date atMonthPosition:(FSCalendarMonthPosition)monthPosition
{
    NSLog(@"did deselect date: %@",[self.dateFormatter stringFromDate:date]);
    [self configureVisibleCells];
}
辅助方法
配置cell
- (void)configureCell:(FSCalendarCell *)cell forDate:(NSDate *)date atMonthPosition:(FSCalendarMonthPosition)monthPosition
{
    
    DIYCalendarCell *diyCell = (DIYCalendarCell *)cell;
    
    // 自定义今日圆圈,如果是今日则显示红色圆圈
    diyCell.circleImageView.hidden = ![self.gregorian isDateInToday:date];
    
    // 配置选择图层即边框位置
    if (monthPosition == FSCalendarMonthPositionCurrent)
    {
        SelectionType selectionType = SelectionTypeNone;
        
        NSLog(@"选中的日期包括:%@",self.calendar.selectedDates);
        if ([self.calendar.selectedDates containsObject:date])
        {
            NSDate *previousDate = [self.gregorian dateByAddingUnit:NSCalendarUnitDay value:-1 toDate:date options:0];
            NSDate *nextDate = [self.gregorian dateByAddingUnit:NSCalendarUnitDay value:1 toDate:date options:0];
            
            if ([self.calendar.selectedDates containsObject:date])
            {
                if ([self.calendar.selectedDates containsObject:previousDate] && [self.calendar.selectedDates containsObject:nextDate])
                {
                    // 选中日期包含昨天和明天则居中
                    selectionType = SelectionTypeMiddle;
                }
                else if ([self.calendar.selectedDates containsObject:previousDate] && [self.calendar.selectedDates containsObject:date])
                {
                    // 选中日期包含昨天和今天则边框居于右边
                    selectionType = SelectionTypeRightBorder;
                }
                else if ([self.calendar.selectedDates containsObject:nextDate])
                {
                    // 选中日期包含明天和今天则边框居于左边
                    selectionType = SelectionTypeLeftBorder;
                }
                else
                {
                    // 只包含今天则只有一个边框
                    selectionType = SelectionTypeSingle;
                }
            }
        }
        else
        {
            // 未选中
            selectionType = SelectionTypeNone;
            NSLog(@"未选中");
        }
        
        // 未选中则隐藏选中图层直接返回
        if (selectionType == SelectionTypeNone)
        {
            diyCell.selectionLayer.hidden = YES;
            NSLog(@"未选中则隐藏选中图层");
            return;
        }
        
        // 选中则显示并更新选中图层
        diyCell.selectionLayer.hidden = NO;
        diyCell.selectionType = selectionType;
        
        NSLog(@"选中则显示并更新选中图层");
        
    }
    else
    {
        // 不是当前处理的日期月份页面(比如下一个月)则直接隐藏圆圈和选中图层
        diyCell.circleImageView.hidden = YES;
        diyCell.selectionLayer.hidden = YES;
        
        NSLog(@"不是当前处理的日期月份页面(比如下一个月)则直接隐藏圆圈和选中图层");
    }
}

3、Prev-Next-Buttons

IOS框架使用:常见框架,第8张
Prev-Next-Buttons
翻到上一页
- (void)previousClicked:(id)sender
{
    NSDate *currentMonth = self.calendar.currentPage;
    NSDate *previousMonth = [self.gregorian dateByAddingUnit:NSCalendarUnitMonth value:-1 toDate:currentMonth options:0];
    [self.calendar setCurrentPage:previousMonth animated:YES];
}
翻到下一页
- (void)nextClicked:(id)sender
{
    NSDate *currentMonth = self.calendar.currentPage;
    NSDate *nextMonth = [self.gregorian dateByAddingUnit:NSCalendarUnitMonth value:1 toDate:currentMonth options:0];
    [self.calendar setCurrentPage:nextMonth animated:YES];
}

4、Hide Placeholder

IOS框架使用:常见框架,第9张
Hide Placeholder
配置日历属性
calendar.placeholderType = FSCalendarPlaceholderTypeNone;// 隐藏占位日期
calendar.adjustsBoundingRectWhenChangingMonths = YES;// 当月份改变的时候自动调整矩形宽度
calendar.currentPage = [self.dateFormatter dateFromString:@"2020-06-01"];
calendar.firstWeekday = 2;// 以周一开始
calendar.scrollDirection = FSCalendarScrollDirectionVertical;// 垂直翻转月份

5、Delegate Appearance

IOS框架使用:常见框架,第10张
Delegate Appearance
初始化颜色
// 默认颜色
self.fillDefaultColors = @{@"2020/10/08":[UIColor purpleColor],// 紫色
                             @"2020/10/06":[UIColor greenColor],// 绿色
// 选中的颜色
self.fillSelectionColors = @{@"2020/10/08":[UIColor greenColor],// 选中后变为绿色
                         @"2020/10/06":[UIColor purpleColor],// 选中后变为紫色
// 边框的颜色
self.borderDefaultColors = @{@"2020/10/08":[UIColor brownColor],// 棕色
// 边框选中的颜色
self.borderSelectionColors = @{@"2020/10/08":[UIColor redColor],// 红色
// 带有事件的Date
self.datesWithEvent = @[@"2020-10-03",// 一个点
// 带有多个事件的Date
self.datesWithMultipleEvents = @[@"2020-10-08",// 多个点
FSCalendarDelegateAppearance
日期事件的颜色组
- (NSArray *)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance eventDefaultColorsForDate:(NSDate *)date
{
    NSString *dateString = [self.dateFormatter2 stringFromDate:date];
    if ([_datesWithMultipleEvents containsObject:dateString])
    {
        return @[[UIColor magentaColor],appearance.eventDefaultColor,[UIColor blackColor]];
    }
    return nil;
}
选中的颜色
- (UIColor *)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance fillSelectionColorForDate:(NSDate *)date
{
    NSString *key = [self.dateFormatter1 stringFromDate:date];
    if ([_fillSelectionColors.allKeys containsObject:key])
    {
        return _fillSelectionColors[key];
    }
    return appearance.selectionColor;
}
默认的颜色
- (UIColor *)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance fillDefaultColorForDate:(NSDate *)date
{
    NSString *key = [self.dateFormatter1 stringFromDate:date];
    if ([_fillDefaultColors.allKeys containsObject:key])
    {
        return _fillDefaultColors[key];
    }
    return nil;
}
边框的颜色
- (UIColor *)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance borderDefaultColorForDate:(NSDate *)date
{
    NSString *key = [self.dateFormatter1 stringFromDate:date];
    if ([_borderDefaultColors.allKeys containsObject:key])
    {
        return _borderDefaultColors[key];
    }
    return appearance.borderDefaultColor;
}
边框选中的颜色
- (UIColor *)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance borderSelectionColorForDate:(NSDate *)date
{
    NSString *key = [self.dateFormatter1 stringFromDate:date];
    if ([_borderSelectionColors.allKeys containsObject:key])
    {
        return _borderSelectionColors[key];
    }
    return appearance.borderSelectionColor;
}
边框的圆角大小
- (CGFloat)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance borderRadiusForDate:(nonnull NSDate *)date
{
    if ([@[@8,@17,@21,@25] containsObject:@([self.gregorian component:NSCalendarUnitDay fromDate:date])])
    {
        return 0.0;// 矩形
    }
    return 1.0;// 圆形
}
FSCalendarDataSource
日期包含的事件个数
- (NSInteger)calendar:(FSCalendar *)calendar numberOfEventsForDate:(NSDate *)date
{
    NSString *dateString = [self.dateFormatter2 stringFromDate:date];
    if ([_datesWithEvent containsObject:dateString])
    {
        return 1;
    }
    if ([_datesWithMultipleEvents containsObject:dateString])
    {
        return 3;
    }
    return 0;
}

6、Full Screen

IOS框架使用:常见框架,第11张
Full Screen
#import <EventKit/EventKit.h>// 事件

@property (strong, nonatomic) NSArray<EKEvent *> *events;

Privacy - Calendars Usage Description 需要使用日历权限
根据国历日期获得对应农历日期
创建中国日历
- (instancetype)init
{
    self = [super init];
    if (self)
    {
        self.chineseCalendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierChinese];
        self.formatter = [[NSDateFormatter alloc] init];
        self.formatter.calendar = self.chineseCalendar;
        self.formatter.dateFormat = @"M";
        self.lunarDays = @[@"初二",@"初三",@"初四",@"初五",@"初六",@"初七",@"初八",@"初九",@"初十",@"十一",@"十二",@"十三",@"十四",@"十五",@"十六",@"十七",@"十八",@"十九",@"二十",@"二一",@"二二",@"二三",@"二四",@"二五",@"二六",@"二七",@"二八",@"二九",@"三十"];
        self.lunarMonths = @[@"正月",@"二月",@"三月",@"四月",@"五月",@"六月",@"七月",@"八月",@"九月",@"十月",@"冬月",@"腊月"];
    }
    return self;
}
根据国历日期获得对应农历日期
- (NSString *)stringFromDate:(NSDate *)date
{
    NSInteger day = [self.chineseCalendar component:NSCalendarUnitDay fromDate:date];
    if (day != 1)
    {
        return self.lunarDays[day-2];// 不是第一天则返回对应农历Day
    }
    
    // First day of month
    NSString *monthString = [self.formatter stringFromDate:date];
    if ([self.chineseCalendar.veryShortMonthSymbols containsObject:monthString])
    {
        return self.lunarMonths[monthString.integerValue-1];
    }
    
    // 闰月
    NSInteger month = [self.chineseCalendar component:NSCalendarUnitMonth fromDate:date];
    monthString = [NSString stringWithFormat:@"闰%@", self.lunarMonths[month-1]];
    return monthString;
}
从系统日历中获取限定日期范围内的日历事件
- (void)loadCalendarEvents
{
    __weak typeof(self) weakSelf = self;
    EKEventStore *store = [[EKEventStore alloc] init];
    [store requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error) {
        
        if(granted)
        {
            NSDate *startDate = self.minimumDate;
            NSDate *endDate = self.maximumDate;
            
            NSPredicate *fetchCalendarEvents = [store predicateForEventsWithStartDate:startDate endDate:endDate calendars:nil];
            NSArray<EKEvent *> *eventList = [store eventsMatchingPredicate:fetchCalendarEvents];
            NSArray<EKEvent *> *events = [eventList filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(EKEvent * _Nullable event, NSDictionary<NSString *,id> * _Nullable bindings) {
                return event.calendar.subscribed;
            }]];
            
            dispatch_async(dispatch_get_main_queue(), ^{
                if (!weakSelf) return;
                weakSelf.events = events;
                [weakSelf.calendar reloadData];
            });
            
        }
        else
        {
            UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"权限错误" message:@"获取事件需要日历权限" preferredStyle:UIAlertControllerStyleAlert];
            [alertController addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil]];
            [self presentViewController:alertController animated:YES completion:nil];
        }
    }];
    
}
从日期获取对应事件并加入缓存
- (NSArray<EKEvent *> *)eventsForDate:(NSDate *)date
{
    NSArray<EKEvent *> *events = [self.cache objectForKey:date];
    if ([events isKindOfClass:[NSNull class]])
    {
        return nil;
    }
    
    // 过滤事件
    NSArray<EKEvent *> *filteredEvents = [self.events filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(EKEvent * _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
        // 判断条件是发生的日期相等
        return [evaluatedObject.occurrenceDate isEqualToDate:date];
    }]];
    
    // 将事件加入缓存
    if (filteredEvents.count)
    {
        [self.cache setObject:filteredEvents forKey:date];
    }
    else
    {
        [self.cache setObject:[NSNull null] forKey:date];
    }
    return filteredEvents;
}
FSCalendarDelegate
每个日期的事件个数
- (NSInteger)calendar:(FSCalendar *)calendar numberOfEventsForDate:(NSDate *)date
{
    if (!self.showsEvents) return 0;
    if (!self.events) return 0;
    
    NSArray<EKEvent *> *events = [self eventsForDate:date];
    return events.count;
}
每个日期的事件颜色
- (NSArray<UIColor *> *)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance eventDefaultColorsForDate:(NSDate *)date
{
    if (!self.showsEvents) return nil;
    if (!self.events) return nil;
    
    NSArray<EKEvent *> *events = [self eventsForDate:date];
    NSMutableArray<UIColor *> *colors = [NSMutableArray arrayWithCapacity:events.count];
    [events enumerateObjectsUsingBlock:^(EKEvent * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        [colors addObject:[UIColor colorWithCGColor:obj.calendar.CGColor]];
    }];
    return colors.copy;
}
FSCalendarDataSource
每个日期的副标题
- (NSString *)calendar:(FSCalendar *)calendar subtitleForDate:(NSDate *)date
{
    // 显示事件
    if (self.showsEvents)
    {
        EKEvent *event = [self eventsForDate:date].firstObject;
        if (event)
        {
            return event.title;
        }
    }
    
    // 显示农历
    if (self.showsLunar)
    {
        return [self.lunarFormatter stringFromDate:date];
    }
    return nil;
}

九、Toast

IOS框架使用:常见框架,第12张
Toast

1、最简单的Toast

IOS框架使用:常见框架,第13张
最简单的Toast
[self.navigationController.view makeToast:@"Toast"];

2、持续指定时间的Toast

IOS框架使用:常见框架,第14张
持续指定时间的Toast
[self.navigationController.view makeToast:@"持续3秒的Toast" duration:3.0 position:CSToastPositionTop];

3、所有Toast共享的风格

IOS框架使用:常见框架,第15张
所有Toast共享的风格
CSToastStyle *style = [[CSToastStyle alloc] initWithDefaultStyle];
style.messageFont = [UIFont fontWithName:@"Zapfino" size:14.0];
style.messageColor = [UIColor redColor];
style.messageAlignment = NSTextAlignmentCenter;
style.backgroundColor = [UIColor yellowColor];
style.titleColor = [UIColor blackColor];
[CSToastManager setSharedStyle:style];

[self.navigationController.view makeToast:@"This is a piece of toast with a title" duration:2.0 position:CSToastPositionTop title:@"文艺复兴运动" image:[UIImage imageNamed:@"luckcoffee.JPG"] style:style completion:^(BOOL didTap) {
    if (didTap)
    {
        NSLog(@"completion from tap");
    }
    else
    {
        NSLog(@"completion without tap");
    }
}];

4、显示加载中的指示器

IOS框架使用:常见框架,第16张
显示加载中的指示器
if (!self.isShowingActivity)
{
    [self.navigationController.view makeToastActivity:CSToastPositionCenter];
}
else
{
    [self.navigationController.view hideToastActivity];
}
_showingActivity = !self.isShowingActivity;
[tableView reloadData];

5、隐藏Toast

[self.navigationController.view hideToast];
[self.navigationController.view hideAllToasts];

6、Toast是否可消失/可退出

[_tapToDismissSwitch setOn:[CSToastManager isTapToDismissEnabled]];// !!!Dimiss Toast
[_queueSwitch setOn:[CSToastManager isQueueEnabled]];// !!!Queue Toast

- (void)handleTapToDismissToggled
{
    [CSToastManager setTapToDismissEnabled:![CSToastManager isTapToDismissEnabled]];
}

- (void)handleQueueToggled
{
    [CSToastManager setQueueEnabled:![CSToastManager isQueueEnabled]];
}

十、zhPopupController

1、提示框

2020-11-09 16:16:43.370131+0800 UseUIControlFramework[92556:7149086] 城市切换的提示框
2020-11-09 16:59:09.136253+0800 UseUIControlFramework[92556:7149086] 点击了取消按钮:取消
2020-11-09 16:59:09.812307+0800 UseUIControlFramework[92556:7149086] 点击了提交按钮:提交
IOS框架使用:常见框架,第17张
垂直提示框
IOS框架使用:常见框架,第18张
水平提示框
创建提示框
- (zhPopupController *)switchCitiesStyle
{
    if (!_switchCitiesStyle)
    {
        CustomAlertView *alertView = [self createHorizontalAlertView];
        
        // 展示方式为提示框
        _switchCitiesStyle = [[zhPopupController alloc] initWithView:alertView size:alertView.bounds.size];
        _switchCitiesStyle.presentationStyle = zhPopupSlideStyleTransform;
        _switchCitiesStyle.presentationTransformScale = 1.25;
        _switchCitiesStyle.dismissonTransformScale = 0.85;
    }
    return _switchCitiesStyle;
}
点击按钮呈现提示框
- (void)test1
{
    [self.switchCitiesStyle showInView:self.view.window completion:^{
        NSLog(@"城市切换的提示框");
    }];
}

2、Toast

2020-11-11 11:38:04.418462+0800 UseUIControlFramework[36230:8555649] 询问喜好的Toast
IOS框架使用:常见框架,第19张
Toast
创建Toast
- (zhPopupController *)toastStyle
{
    if (!_toastStyle)
    {
        CustomAlertView *alertView = [self createFavouriteToastView];
        
        // 展示方式为Toast
        _toastStyle = [[zhPopupController alloc] initWithView:alertView size:alertView.bounds.size];
        _toastStyle.presentationStyle = zhPopupSlideStyleTransform;
        _toastStyle.maskType = zhPopupMaskTypeDarkBlur;// 黑色模糊遮罩
        _toastStyle.dismissOnMaskTouched = NO;// 点击黑色模糊遮罩则Toast消失
    }
    return _toastStyle;
}
点击按钮呈现Toast
- (void)test2
{
    // 0.75秒后自动消失
    [self.toastStyle showInView:self.view.window duration:0.75 bounced:YES completion:^{
        NSLog(@"询问喜好的Toast");
    }];
}

3、带图片的提示框

2020-11-11 15:14:44.412847+0800 UseUIControlFramework[39522:8695494] 展示火箭视图
2020-11-11 15:14:45.804308+0800 UseUIControlFramework[39522:8695494] 点击了查看详情
IOS框架使用:常见框架,第20张
带图片的Alert View
创建火箭弹出视图
- (zhPopupController *)overflyStyle
{
    if (!_overflyStyle)
    {
        OverflyView *overflyView = [self createOverflyView];
        
        _overflyStyle = [[zhPopupController alloc] initWithView:overflyView size:overflyView.bounds.size];
        _overflyStyle.dismissOnMaskTouched = NO;
        // 从底部滑出
        _overflyStyle.presentationStyle = zhPopupSlideStyleFromBottom;
        // 从顶部消失
        _overflyStyle.dismissonStyle = zhPopupSlideStyleFromTop;
        // 控制弹出视图偏离的位置
        _overflyStyle.offsetSpacing = 20;
    }
    return _overflyStyle;
}
点击展示火箭视图
- (void)test3
{
    [self.overflyStyle showInView:self.view.window completion:^{
        NSLog(@"展示火箭视图");
    }];
}

4、窗帘

IOS框架使用:常见框架,第21张
窗帘
创建窗帘弹出视图
- (zhPopupController *)qzoneStyle
{
    if (!_qzoneStyle)
    {
        CurtainView *curtainView = [self createCurtainView];
        
        _qzoneStyle = [[zhPopupController alloc] initWithView:curtainView size:curtainView.bounds.size];
        _qzoneStyle.layoutType = zhPopupLayoutTypeTop;// 位置在顶部
        _qzoneStyle.presentationStyle = zhPopupSlideStyleFromTop;// 从顶部滑入呈现
        _qzoneStyle.offsetSpacing = -30;// 偏移量
        
        __weak typeof(self) weakSelf = self;
        // 窗帘即将呈现的时候状态栏文字变暗
        _qzoneStyle.willPresentBlock = ^(zhPopupController * _Nonnull popupController) {
            weakSelf.isLightStatusBar = NO;
        };
        
        // 窗帘即将消失的时候状态栏文字变亮
        _qzoneStyle.willDismissBlock = ^(zhPopupController * _Nonnull popupController) {
            weakSelf.isLightStatusBar = YES;
        };
    }
    return _qzoneStyle;
}
点击后弹出窗帘视图
- (void)test4
{
    [self.qzoneStyle showInView:self.view.window duration:0.75 bounced:YES completion:^{
        NSLog(@"点击后弹出窗帘视图");
    }];
}
提供窗帘视图按钮的点击事件
- (CurtainView *)createCurtainView
{
    CurtainView *curtainView = [self curtainView];
    
    __weak typeof(self) weakSelf = self;
    // 点击后窗帘消失
    curtainView.closeClicked = ^(UIButton *closeButton) {
        [weakSelf.qzoneStyle dismiss];
    };
    
    // 点击后弹出提示框
    curtainView.didClickItems = ^(CurtainView *curtainView, NSInteger index) {
        [self showAlert:curtainView.items[index].titleLabel.text];
    };
    
    return curtainView;
}
弹出提示框
- (void)showAlert:(NSString *)text
{
    UILabel *label = [UILabel new];
    label.backgroundColor = [UIColor whiteColor];
    label.frame = CGRectMake(0, 0, 270, 70);
    label.numberOfLines = 0;
    label.textAlignment = NSTextAlignmentCenter;
    label.layer.cornerRadius = 3;
    label.layer.masksToBounds = YES;
    label.text = text;
    label.font = [UIFont fontWithName:@"palatino-boldItalic" size:20];
    
    zhPopupController *popupController = [[zhPopupController alloc] initWithView:label size:label.bounds.size];
    popupController.dismissAfterDelay = 1;// 1秒后消失
    popupController.maskType = zhPopupMaskTypeBlackOpacity;// 黑色遮罩
    popupController.presentationStyle = zhPopupSlideStyleTransform;
    popupController.layoutType = zhPopupLayoutTypeTop;// 位置在顶部
    popupController.offsetSpacing = 90;// 偏移量
    
    UIView *window = UIApplication.sharedApplication.keyWindow;
    [popupController showInView:window duration:0.55 delay:0 options:UIViewAnimationOptionCurveLinear bounced:YES completion:nil];
}

5、侧边栏

IOS框架使用:常见框架,第22张
侧边栏
创建侧边栏视图
- (zhPopupController *)sidebarStyle
{
    if (!_sidebarStyle)
    {
        SidebarView *sidebar = [self createSidebarView];
        _sidebarStyle = [[zhPopupController alloc] initWithView:sidebar size:sidebar.bounds.size];
        _sidebarStyle.layoutType = zhPopupLayoutTypeLeft;// 布局在左边
        _sidebarStyle.presentationStyle = zhPopupSlideStyleFromLeft;// 从左边出现
        _sidebarStyle.panGestureEnabled = YES;// 支持扇动手势
        _sidebarStyle.panDismissRatio = 0.5;// 到Sidebar出现的比例少于自身一半的时候则隐藏
    }
    
    return _sidebarStyle;
}
点击后弹出侧边栏
- (void)test5
{
    [self.sidebarStyle showInView:self.view.window completion:^{
        NSLog(@"点击后弹出侧边栏");
    }];
}
实现侧边栏按钮的点击事件
- (SidebarView *)createSidebarView
{
    SidebarView *sidebar = [self sidebarView];
    
    __weak typeof(self) weakSelf = self;
    sidebar.didClickItems = ^(SidebarView *sidebarView, NSInteger index) {
        [weakSelf.sidebarStyle dismiss];
        [self showAlert:sidebarView.items[index].titleLabel.text];
    };
    
    return sidebar;
}

6、全屏视图

IOS框架使用:常见框架,第23张
全屏视图
创建全屏视图
- (zhPopupController *)fullStyle
{
    if (!_fullStyle)
    {
        FullView *fullView = [self createFullView];
        _fullStyle = [[zhPopupController alloc] initWithView:fullView size:fullView.bounds.size];
        _fullStyle.maskType = zhPopupMaskTypeExtraLightBlur;
        _fullStyle.willPresentBlock = ^(zhPopupController * _Nonnull popupController) {
            [fullView startAnimationsWithCompletion:^(BOOL finished) {
                NSLog(@"弹出全屏视图完成");
            }];
        };
    }
    return _fullStyle;
}
点击弹出全屏视图
- (void)test6
{
    [self.fullStyle showInView:self.view.window completion:^{
        NSLog(@"点击弹出全屏视图");
    }];
}

7、分享视图

IOS框架使用:常见框架,第24张
分享视图
创建分享视图
- (zhPopupController *)shareStyle
{
    if (!_shareStyle)
    {
        WallView *wallView = [self createShareView];
        _shareStyle = [[zhPopupController alloc] initWithView:wallView size:wallView.bounds.size];
        _shareStyle.layoutType = zhPopupLayoutTypeBottom;
        _shareStyle.presentationStyle = zhPopupSlideStyleFromBottom;
    }
    return _shareStyle;
}
点击弹出分享视图
- (void)test7
{
    [self.shareStyle showInView:self.view.window duration:0.35 delay:0 options:UIViewAnimationOptionCurveEaseInOut bounced:NO completion:nil];
}

8、键盘视图

IOS框架使用:常见框架,第25张
登录键盘
IOS框架使用:常见框架,第26张
注册键盘
IOS框架使用:常见框架,第27张
评论键盘
创建登陆注册视图
- (zhPopupController *)centerKeyboardStyle
{
    if (!_centerKeyboardStyle)
    {
        UIView *backView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 236)];
        [backView addSubview:self.centerKeyboardView];

        _centerKeyboardStyle = [[zhPopupController alloc] initWithView:backView size:backView.bounds.size];
        _centerKeyboardStyle.maskType = zhPopupMaskTypeBlackOpacity;
        _centerKeyboardStyle.layoutType = zhPopupLayoutTypeCenter;
        _centerKeyboardStyle.presentationStyle = zhPopupSlideStyleFromBottom;
        _centerKeyboardStyle.keyboardOffsetSpacing = 50;// 调整键盘间距
        _centerKeyboardStyle.keyboardChangeFollowed = YES;// YES将在键盘更改时调整视图位置
        _centerKeyboardStyle.becomeFirstResponded = YES;
        
        __weak typeof(self) weakSelf = self;
        _centerKeyboardStyle.willPresentBlock = ^(zhPopupController * _Nonnull popupController) {
            [weakSelf.centerKeyboardView.phoneNumberField becomeFirstResponder];
        };
        
        _centerKeyboardStyle.willDismissBlock = ^(zhPopupController * _Nonnull popupController) {
            if (weakSelf.centerKeyboardView.phoneNumberField.isFirstResponder)
            {
                [weakSelf.centerKeyboardView.phoneNumberField resignFirstResponder];
            }
            
            if (weakSelf.registerKeyboardView.phoneNumberField.isFirstResponder)
            {
                [weakSelf.registerKeyboardView.phoneNumberField resignFirstResponder];
            }
        };
    }
    return _centerKeyboardStyle;
}
创建评论视图
- (zhPopupController *)commentKeyboardStyle
{
    if (!_commentKeyboardStyle)
    {
        CGRect rect = CGRectMake(0, 0, self.view.width, 60);
        CommentKeyboardView *commentKeyboardView = [[CommentKeyboardView alloc] initWithFrame:rect];
        
        __weak typeof(self) weakSelf = self;
        commentKeyboardView.senderClickedBlock = ^(CommentKeyboardView *keyboardView, UIButton *button) {
            [weakSelf.commentKeyboardStyle dismiss];
        };
        
        _commentKeyboardStyle = [[zhPopupController alloc] initWithView:commentKeyboardView size:commentKeyboardView.bounds.size];
        _commentKeyboardStyle.maskType = zhPopupMaskTypeDarkBlur;
        _commentKeyboardStyle.layoutType = zhPopupLayoutTypeBottom;
        _commentKeyboardStyle.presentationStyle = zhPopupSlideStyleFromBottom;
        _commentKeyboardStyle.becomeFirstResponded = YES;
        _commentKeyboardStyle.keyboardChangeFollowed = YES;
        
        _commentKeyboardStyle.willPresentBlock = ^(zhPopupController * _Nonnull popupController) {
            [commentKeyboardView.commentTextField becomeFirstResponder];
        };
        
        _commentKeyboardStyle.willDismissBlock = ^(zhPopupController * _Nonnull popupController) {
            [commentKeyboardView.commentTextField resignFirstResponder];
        };
    }
    return _commentKeyboardStyle;
}

Demo

Demo在我的Github上,欢迎下载。
UseFrameworkDemo

参考文献


https://www.xamrdz.com/web/2nu1994064.html

相关文章: