作者 ShevaKuilin 关注

2017.03.25 18:28* 字数 2014 阅读 270评论 0喜欢 23

さらい屋五葉

原文地址----> MyBlog

HUD风格的选项弹窗是我们在日常开发中经常会碰到的一类需求,通常因为项目周期等因素,很少会专门抽出时间来对此类弹窗进行专门的定制开发和维护。常见的情况就是google类似的效果控件,如果恰好匹配需求,效果上说得过去,那么便可以节省不少的时间和精力,但更多的情况是,我们花费了更多的时间去修改、去填坑,效果缺不见得如意,导致开发者最后不得不吐槽:还不如自己写。对于注重追求效率的开发者,似乎什么轮子都可以上路跑,只要它ok,但对于更注重用户体验和效果的开发者而言,就很难将就了,但人的精力总归有限,抽不开身怎么办呢?这个时候就很需要一款效果很赞,使用方便、简洁、可快速集成的组件了:SKChoosePopView便应运而生了。

简述

SKChoosePopView是一个HUD风格的可定制化选项弹窗的快速解决方案,集成了上、下、左、右、中5个进场方向的6种动画效果,如果不能满足你对酷炫效果的需要,SKChoosePopView同样支持自定义动画,以及选择记录、动画的开闭、点击特效、行列数量控制等。如果你觉得还不错,star支持一下吧!

效果图

一.如何使用

1.如何开始

1.从GitHub上Clone-->SKChoosePopView, 然后查看Demo

2.请仔细阅读下方特别指出的部分和需要注意问题

3.在项目中使用SKChoosePopView,直接将SKPopView文件夹拷贝到工程中

4.SKChoosePopView基于Masonry布局,请确保你的工程里已存在Masonry,下载地址

2.使用方法

头文件导入

#import "SKPopView.h"

初始化

/** 初始化方法
* @param title 标题数组
* @param iconNormal 默认图标数组
* @param iconSelected 选中图标数组, 若不需要传入nil即可
* @param titleColor 选中的选项标题字体颜色, 若不需要传入nil即可
* @param delegate 代理协议
* @param completion 弹窗出现后的回调操作,若不需要传入nil即可
*/
SKPopView * popView = [[SKPopView alloc] initWithOptionsTitle:kDate.title OptionsIconNormal:kDate.normalIconsOptionsIconSelected:kDate.selectedIcons selectedTitleColor:[UIColor orangeColor] delegate:self  completion:^{// TODO: 如果这里不需要就nil
}];

显示

[popView show];

消失

[popView dismiss];

设置动画类型

popView.animationType = SK_TYPE_SPRING;

设置动画方向

popView.animationDirection = SK_SUBTYPE_FROMBOTTOM;

动画时间

popView.animationDuration = 0.5;

开启/关闭选择记录

popView.enableRecord = YES;

开启/关闭动画效果

popView.enableAnimation = YES;

行数设置

popView.optionsLine = 2;

列数设置

popView.optionsRow = 3;

最小行间距

popView.minLineSpacing = 10;

最小列间距

popView.minRowSpacing = 10;

动画设置

SK_TYPE_SPRING,// 弹簧效果
SK_TYPE_ROTATION,// 旋转效果
SK_TYPE_FADE,// 渐变效果
SK_TYPE_LARGEN,// 变大效果
SK_TYPE_ROTATION_LARGEN,// 旋转变大效果
SK_TYPE_TRANSFORMATION// 变形效果

动画进场方向

SK_SUBTYPE_FROMRIGHT,// 从右侧进入
SK_SUBTYPE_FROMLEFT,// 从左侧进入
SK_SUBTYPE_FROMTOP,// 从顶部进入
SK_SUBTYPE_FROMBOTTOM,// 从底部进入
SK_SUBTYPE_FROMCENTER// 从屏幕中间进入

获取已选择的选项row(代理协议方法)

- (void)selectedWithRow:(NSUInteger)row;

注意事项

1.optionsLineoptionsRow属性是必须设置的, 且遵循垂直布局原则,请确保optionsLine * optionsRow于选项数量相等

2.最小行、列间距如不需要可以不设置,默认为0

3.如果开启动画,请确保animationTypeanimationDirectionanimationDuration属性已经设置

4.使用代理协议方法前,请确保已遵循<SKPopViewDelegate>

4.如果遇到其它问题,欢迎提交issues,我会及时回复

二.实现思路

1.设计思路

  • 首先,在总体设计上我们采取模块化的思路,主要分为两部分:SKPopView(视图)和SKPopAnimationManger(动画管理),各司其职。

  • SKPopView负责处理界面控件的创建和布局约束、外部对弹窗配置信息设置的响应(如是否开启动画效果、点击效果、选择记录、动画类型/方向、需要显示的行/列等)、已选择的选项回调等。

  • SKPopAnimationManger则负责动画的相关管理,如对动画进场方向的轨迹控制、动画效果的具体实现等。

2.功能实现

布局

  • 在整体上,SKPopView充当了一个父UIView的角色,在其内部添加grayBackground1(灰色的背景)和popView(HUD弹窗部分),总体结构简单明了,所以在调用的时候我们是直接将SKPopView整个添加到我们需要显示的界面当中来使用的。

  • SKPopView中,grayBackground作为灰色背景率先addSubview到SKPopView[self addSubview:grayBackground]
    ,而弹窗则作为插入部分,插入到grayBackground之上[self insertSubview:self.popView aboveSubview:grayBackground], 而在popView内部,我们内嵌了一个UICollectionView,作为选项按钮的展示。

弹窗的出现与消失

  • 弹窗的出现原理:当弹窗被调用时,SKPopView整体便会添加到当前视图的图层之上,而弹窗popView此时会根据调用时在初始化方法里配置的SK_TYPE(动画类型)和SK_SUBTYPE(动画方向),在屏幕外选择一个入场前的坐标和入场时的动画效果,当初始化完成开始调用SKPopViewshow方法时,popView便开始入场、完成动画。
#pragma mark - 外部调用
- (void)show
{if (self.enableAnimation == YES) {// 如果开启动画效果[self displayAnimation];}
}
#pragma mark - 动画设置
- (void)displayAnimation
{self.animationManger = [[SKPopAnimationManger alloc] init];switch (self.animationType) {// 动画类型case SK_TYPE_SPRING:self.animationManger.type = SK_ANIMATION_TYPE_SPRING;break;case SK_TYPE_ROTATION:self.animationManger.type = SK_ANIMATION_TYPE_ROTATION;break;case SK_TYPE_FADE:self.animationManger.type = SK_ANIMATION_TYPE_FADE;break;case SK_TYPE_LARGEN:self.animationManger.type = SK_ANIMATION_TYPE_LARGEN;break;case SK_TYPE_ROTATION_LARGEN:self.animationManger.type = SK_ANIMATION_TYPE_ROTATION_LARGEN;break;case SK_TYPE_TRANSFORMATION:self.animationManger.type = SK_ANIMATION_TYPE_TRANSFORMATION;break;
}switch (self.animationDirection) {// 动画进场方向case SK_SUBTYPE_FROMRIGHT:self.animationManger.animationDirection = SK_ANIMATION_SUBTYPE_FROMRIGHT;break;case SK_SUBTYPE_FROMLEFT:self.animationManger.animationDirection = SK_ANIMATION_SUBTYPE_FROMLEFT;break;case SK_SUBTYPE_FROMTOP:self.animationManger.animationDirection = SK_ANIMATION_SUBTYPE_FROMTOP;break;case SK_SUBTYPE_FROMBOTTOM:self.animationManger.animationDirection = SK_ANIMATION_SUBTYPE_FROMBOTTOM;break;case SK_SUBTYPE_FROMCENTER:self.animationManger.animationDirection = SK_ANIMATION_SUBTYPE_FROMCENTER;break;default:break;
}// 对进场动画进行设置[self.animationManger animateWithView:self.popView Duration:self.animationDuration animationType:self.animationManger.type animationDirection:self.animationManger.animationDirection];
}
  • 由于支持对已选择的按钮进行记录保存,所以在使用上,用户仅能通过对灰色背景的点击来达到让弹窗消失的目的,我们通过对grayBackground添加点击手势来达到目的, 注意:grayBackground在这里是一个UIImageView,在添加手势时,一定要将userInteractionEnabled = YES,开启用户交互,否则手势将失效。
// 灰色背景
UIImageView * grayBackground = [UIImageView new];
[self addSubview:grayBackground];
grayBackground.backgroundColor = [UIColor colorWithWhite:0.3 alpha:0.5];;
grayBackground.userInteractionEnabled = YES;
[grayBackground mas_makeConstraints:^(MASConstraintMaker *make) {make.edges.equalTo(self).with.insets(UIEdgeInsetsMake(0, 0, 0, 0));
}];
// 添加手势
UITapGestureRecognizer * dismissGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(cancel)];
[grayBackground addGestureRecognizer:dismissGesture];
#pragma mark - 手势响应
- (void)cancel
{[self dismissAnimation];
}
- (void)dismissAnimation
{if (self.enableAnimation == YES) {// 如果开启动画效果[self.animationManger dismissAnimationForRootView:self];// 使用离场动画} else {[self removeFromSuperview];// 直接移除整个SKPopView}
}
  • 如果需求上有取消按钮,可以在取消按钮的点击方法内手动调用SKPopViewdismiss方法,十分方便。

- (void)clickCancel // 取消按钮方法
{[popView dismiss];
}

选项的数量与显示把控

  • 因为我们在popView中镶嵌了一个UICollectionView,而UICollectionView提供了几个很好的方法可以让我们相对轻松的对选项的数量及在弹窗内的显示方式进行把控,我们主要应用了下面几个方法
// 设置item的size
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
// 最小列(横向)间距
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
// 最小行(纵向)间距
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section

然后根据我们在SKPopView的头文件中暴露的外部配置信息optionsLine(显示的行数)、optionsRow(显示的列数)、minLineSpacing(最小行间距)、minRowSpacing(最小列间距),来对具体的样式进行修改,具体转换算法请参考SKPopView.m中外部配置标签下的代码段

动画

  • 动画部分我们分为:进场动画、撤场动画和选项点击动画三个部分

1.进场动画:

  • 首先我们要弄明白一点,进场动画,顾名思义:就是由进场+动画两个效果部分组成的

  • 由于默认集成了来自5个方向的6种不同效果的动画,导致动画在进行相应配置时,对于每种动画效果都需要考虑5种不同的进场起始坐标,所以,我们用一个统一的方法对动画的进场方向进行统一管理,同样的,考虑到一些动画效果需要使用动画组, 对动画在进场过程中的位移路径也需要做一个统一管理

    #pragma mark - 动画方向初始化- (void)animationDirectionInitialize{if (_animationDirection == SK_ANIMATION_SUBTYPE_FROMRIGHT) {// 从右侧进场_animationView.center = CGPointMake(MyWidth + 1000, WindowCenter.y);} else if (_animationDirection == SK_ANIMATION_SUBTYPE_FROMLEFT) {// 从左侧进场_animationView.center = CGPointMake(MyWidth - 1000, WindowCenter.y);} else if (_animationDirection == SK_ANIMATION_SUBTYPE_FROMTOP) {// 从顶部进场_animationView.center = CGPointMake(WindowCenter.x, MyHeight - 1000);} else if (_animationDirection == SK_ANIMATION_SUBTYPE_FROMBOTTOM) {// 从底部进场_animationView.center = CGPointMake(WindowCenter.x, MyHeight + 1000);} else {// 中间进场_animationView.center = WindowCenter;}}#pragma mark - 动画通用位移- (CAKeyframeAnimation *)partOfTheAnimationGroupPosition:(CGPoint)startPosition{CAKeyframeAnimation * animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];// 创建关键帧位移动画NSValue * startValue, * endValue;startValue = [NSValue valueWithCGPoint:startPosition];// 起始坐标endValue = [NSValue valueWithCGPoint:WindowCenter];// 结束坐标animation.values = @[startValue, endValue];// 设置动画路径animation.duration = _animationDuration;// 持续时间return animation;}
  • 其中弹簧效果的动画由于不是并发动画效果,所以是没有用到通用位移的,所以它的位移方法是独立出来的
    #pragma mark - 弹簧效果- (void)springAnimation{[self animationDirectionInitialize];// 初始化方向[self displacementWithStartPosition:_animationView.center];}/** 弹簧效果位移部分* @param startPosition 位移起始坐标*/- (void)displacementWithStartPosition:(CGPoint)startPosition{CAKeyframeAnimation * animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];// 创建关键帧动画NSValue * startValue, * endValue;startValue = [NSValue valueWithCGPoint:startPosition];endValue = [NSValue valueWithCGPoint:WindowCenter];animation.values = @[startValue, endValue];animation.duration = _animationDuration;animation.delegate = self;// 设置CAAnimationDelegate[_animationView.layer addAnimation:animation forKey:@"pathAnimation"];}/** 弹簧效果晃动部分*/- (void)partOfTheSpringGroupShaking{NSString * keyPath = @"";if (_animationDirection == SK_ANIMATION_SUBTYPE_FROMLEFT || _animationDirection == SK_ANIMATION_SUBTYPE_FROMRIGHT) {// 判断弹窗的来向keyPath = @"transform.translation.x";} else {keyPath = @"transform.translation.y";}CABasicAnimation * animation = [CABasicAnimation animationWithKeyPath:keyPath];animation.fromValue = [NSNumber numberWithFloat:-20.0];animation.toValue = [NSNumber numberWithFloat:20.0];animation.duration = 0.1;animation.autoreverses = YES;// 是否重复animation.repeatCount = 2;// 重复次数[_animationView.layer addAnimation:animation forKey:@"shakeAnimation"];}- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag// 监听动画停止{[self partOfTheSpringGroupShaking];}

2.撤场动画

  • 撤场动画就相对简单很多, 由于撤场方式是统一的,只需要对撤出坐标做处理,然后removeFromSuperview

    - (void)dismissAnimationForRootView:(UIView *)view
    {[UIView animateWithDuration:0.5 animations:^{_animationView.center = CGPointMake(WindowCenter.x, MyHeight + 1000);} completion:^(BOOL finished) {[view removeFromSuperview];}];
    }

3.选项点击动画

  • 当点击动画效果被开启时,才会调用这个方法,即enableClickEffect = YES时, SKPopViewCollectionViewCell中会调用点击动画

    - (void)setEnableClickEffect:(BOOL)enableClickEffect
    {_enableClickEffect = enableClickEffect;if (enableClickEffect == YES) {SKPopAnimationManger * animationManger = [[SKPopAnimationManger alloc] init];[animationManger clickEffectAnimationForView:self.basementView];}
    }

    而在SKPopAnimationManger中,我们只是巧妙的对其做了一个缩放的效果,即可达到果冻式的触感

    - (void)clickEffectAnimationForView:(UIView *)view
    {CABasicAnimation * scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];scaleAnimation.fromValue = [NSNumber numberWithFloat:1];scaleAnimation.toValue = [NSNumber numberWithFloat:0.8];scaleAnimation.duration = 0.1;scaleAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];[view.layer addAnimation:scaleAnimation forKey:nil];
    }

感谢你花时间阅读以上内容, 如果这个项目能够帮助到你,记得告诉我

Email: shevakuilin@gmail.com

或者直接在文章下留言哦~

两行代码搞定iOS自定义HUD风格动画弹窗(支持选择记录) - SKChoosePopView的使用和实现思路相关推荐

  1. 用Python进行屏幕截图,只用两行代码搞定

    一.计算机中如何进行屏幕截图呢? 1.全屏截图 按下键盘中的'PRTSC'或者'Print Screen'键,即可实现全屏截图(不同键盘位置和名称可能不同).此时,并不能看到效果,只是将截图保存在粘贴 ...

  2. python实现屏幕截图_电脑截屏操作有哪些,用Python进行屏幕截图,只用两行代码搞定...

    对于自媒体作者或者经常编辑进行文本编辑的人来讲,屏幕截图应该是一个很常见的操作.今天,带大家一起体验一下Python屏幕截图的操作,通过这篇内容你可以复习以下知识点: Python截图屏幕图片 使用P ...

  3. 自带的数据集_机器学习练习数据哪里找?两行代码搞定!

    初学者学习机器学习的时候,经常会找不到练习的数据,实际上scikit-learn内置了很多可以用于机器学习的数据,可以用两行代码就可以使用这些数据. 一.自带数据集 自带的小的数据集为:sklearn ...

  4. 机器学习练习数据哪里找?两行代码搞定!

    初学者学习机器学习的时候,经常会找不到练习的数据,实际上scikit-learn内置了很多可以用于机器学习的数据,可以用两行代码就可以使用这些数据. 一.自带数据集 自带的小的数据集为:sklearn ...

  5. ios 自定义拍照页面_30分钟搞定iOS自定义相机

    最近公司的项目中用到了相机,由于不用系统的相机,UI给的相机切图,必须自定义才可以.就花时间简单研究了一下相机的自定义. 相机属于系统硬件,这就需要我们来手动调用iPhone的相机硬件,分为以下步骤: ...

  6. 一气之下,我一行代码搞定了约瑟夫环问题,面试官懵了

    大家好,我是帅地. 对于约瑟夫环问题估计大家都听说过,除非你刚刚读大一,因为在大一大部分学校的课本都会降到这个算法题.为了以防万一你没听过,我还是给下问题的描述 问题描述:编号为 1-N 的 N 个士 ...

  7. 一行代码搞定 Scrapy 随机 User-Agent 设置

    摘要:爬虫过程中的反爬措施非常重要,其中设置随机 User-Agent 是一项重要的反爬措施,Scrapy 中设置随机 UA 的方式有很多种,有的复杂有的简单,本文就对这些方法进行汇总,提供一种只需要 ...

  8. 【Python基础】pandas的骚操作:一行 pandas 代码搞定 Excel “条件格式”!

    来源:Python数据科学 作者:东哥起飞 本篇是pandas100个骚操作系列的第 7 篇:一行 pandas 代码搞定 Excel "条件格式"! 系列内容,请看????「pa ...

  9. python做事件研究法_35行代码搞定事件研究法(上)

    作者简介: 祝小宇,个人公众号:大猫的R语言课堂 这期大猫课堂将会教大家如何用35行R代码写出最有效率的事件研究法. 注意,本代码主要使用data.table完成,关于data.table包的相应知识 ...

最新文章

  1. 项目进度,你汇报清楚了么?
  2. 从EXCHANGE03转移到EXCHANGE07
  3. 2019最具特色的web前端技术分享
  4. Rancher搭建集群
  5. c# DirectoryInfo 类和 FileInfo 类
  6. Autofac在.NET Core 中的使用
  7. 如何让手机快速运行AI应用?这有份TVM优化教程
  8. 立于山巅!他,凭什么抗住万亿级流量冲击!
  9. 鼠标移上图标字体,让其旋转
  10. C++makefile编写
  11. 【项目总结】中国大学生计算机设计(机械制造暨政治正确)大赛
  12. 推鹿是什么?推鹿介绍,推鹿是什么平台?
  13. PHP strpos 函数源码分析
  14. 社交之战,结局:被封?,这只是一个开始
  15. 第十周 项目一 计算税后收入
  16. C语言与JAVA区别
  17. 弘辽科技:拼多多关键词排名有什么用?有什么规则?
  18. (新 ※必看)解决VMware由于卸载不干净导致安装出现“用户在命令行上发出了 EULAS_AGREED=1,表示不接受许可协议。”
  19. 个人笔记:算法讲座3.4——货币系统(完全背包)
  20. Activiti 6研究01 - 流程文件的手动导入

热门文章

  1. Hadoop 分布式环境搭建
  2. 生物信息大数据数据库(NCBI、EBI、UCSC、TCGA)
  3. Junit的安装与使用
  4. css层叠样式表(一)
  5. 超强的jquery极品插件--色彩选择器类/ 右键菜单类/ 图片新闻flash展示类
  6. ASP.NET的(HttpModule,HttpHandler)
  7. 最简便的清空memcache的方法
  8. 以金山界面库(openkui)为例思考和分析界面库的设计和实现——资源读取模块分析
  9. Ubuntu下使用CMake编译OpenSSL源码操作步骤(C语言)
  10. 结构体中最后一个成员为[0]或[1]长度数组(柔性数组成员)的用法