Swift.轮转动画+Pop框架

效果图

前言:

项目改自Swift.轮转动画,100行代码搞定,页面布局没有变化,只是改变了动画效果,以及动画实现方式.所以有关布局的问题可以参考那篇文章.

实现效果:

长按中心View,使周围子View旋转,并将最下方的View放大,获取其image在中心View展示,当停止长按时动画停止.

实现思路:

所有的控件全部加到一个大的背景view上,本质上旋转的是这个背景view,在旋转背景view的同时,让它所有的子控件反向旋转,就实现了现在这种效果.

使用pop框架添加旋转动画.给中心button一个.touchDown手势事件添加动画,再添加一个[.touchUpInside, .touchDragExit]手势事件来移除动画.

最下方的view变大是通过CADisplayLink监听子view.layer.presention().bounds,来获取子View在当前页面的实际frame.x,当它处于一个范围,并且frame.y小于中心view.frame.y的时候.修改它的transform,来使其变大,并且修改它的tag来标记它已经属于变大状态,当它frame.x超出了预定范围,使其还原.

遇到问题:

1.动画实现方式选择问题:

之前有尝试使用transform,CABasicAnimation,UIView.animation三种动画方式:都遇到了各种各样的问题:

  • 使用transform时很难实现长按按钮动画一直运行这个效果.
  • 使用CABasicAnimation时只修改view.layer.frame并不修改view.frame,所以会出现动画暂停时view闪回或者view展示位置与功能位置不符的问题.
  • 使用UIView.animation时很难流畅的控制它的暂停与开始.
    都无法完美实现,所以使用了faceBook提供的pop框架.pop框架在使用上与CABasicAnimation很像,建议可以尝试使用.
2.使用pop动画,无法获取子view的即时frame问题:

想使用KVO监听,但无法实现.
选择使用CADisplayLink监听view.layer.presentation().layer.再通过

let rect = subView.convert(bounds, to: UIApplication.shared.keyWindow)

来获取子View在当前页面中的即时Frame.

实现方式:

1.添加背景透明view,中心圆圈view.

2.添加周围旋转子view.

3.添加旋转方法.

4.修改最下方View的大小,并获取其image赋值给中心View


1.添加背景透明view,中心圆圈view.

 /// 添加背景view,也是旋转的view
    private func setContentView() {
        setCircleView()
        contentView = UIView(frame: CGRect(x: 0, y: 0, width: ScreenInfo.Width, height: ScreenInfo.Width))
        contentView?.center = self.view.center
        self.view.addSubview(contentView!)
        contentView!.addSubview(circleView!)
    }
    /// 添加中间圆形view
    private func setCircleView(){
        let view = UIImageView(frame: CGRect(x: 0.5 * CGFloat(1 - PROPORTION) * ScreenInfo.Width + 10, y: 0.5 * CGFloat(1 - PROPORTION) * ScreenInfo.Width + 10, width: ScreenInfo.Width * CGFloat(PROPORTION) - 20, height: ScreenInfo.Width * CGFloat(PROPORTION) - 20))
        /// 为了适配保证size变化center不变
        let centerPoint = view.center
        view.frame.size = CGSize(width: ScreenInfo.Width * CGFloat(PROPORTION) - 40, height: ScreenInfo.Width * CGFloat(PROPORTION) - 40)
        view.center = centerPoint
        view.image = UIImage(named: "11")
        view.layer.cornerRadius = view.frame.width*0.5
        view.layer.masksToBounds = true
        view.isUserInteractionEnabled = true
        circleView = view
    }

2.添加周围旋转子view.

   /// 布局旋转的子view
    private func rotationCircleCenter(contentOrgin: CGPoint,
                                      contentRadius: CGFloat,subnode: [String]){
        // 添加比例,实现当要添加的子view数量较多时候可以自适应大小.
        var scale: CGFloat = 1
        if subnode.count > 10 {
            scale = CGFloat(CGFloat(subnode.count) / 13.0)
        }

        for i in 0..<subnode.count {
            let x = contentRadius * CGFloat(sin(.pi * 2 / Double(subnode.count) * Double(i)))
            let y = contentRadius * CGFloat(cos(.pi * 2 / Double(subnode.count) * Double(i)))
            // 当子view数量大于10个,view.size变小,防止view偏移,要保证view.center不变.
            let view = EWSubView(frame: CGRect(x:contentRadius + 0.5 * CGFloat((1 + PROPORTION)) * x - 0.5 * CGFloat((1 - PROPORTION)) * contentRadius, y: contentRadius - 0.5 * CGFloat(1 + PROPORTION) * y - 0.5 * CGFloat(1 - PROPORTION) * contentRadius, width: CGFloat((1 - PROPORTION)) * contentRadius, height: CGFloat((1 - PROPORTION)) * contentRadius), imageName: subnode[i])
            let centerPoint = view.center
            view.frame.size = CGSize(width: CGFloat((1 - PROPORTION)) * contentRadius / scale , height: CGFloat((1 - PROPORTION)) * contentRadius / scale)
            view.center = centerPoint
            view.drawSubView()
            // 这个tag判断view是不是在最下方变大状态,非变大状态0,变大为1
            view.tag = 0
            // 获取子view在当前屏幕中的rect.来实现在最下方的那个变大
            let rect = view.convert(view.bounds, to: UIApplication.shared.keyWindow)
            let viewCenterX = rect.origin.x + (rect.width) / 2
            if viewCenterX > self.view.center.x - 20 && viewCenterX < self.view.center.x + 20 && rect.origin.y > (contentView?.center.y)! {
                view.transform = view.transform.scaledBy(x: 1.5, y: 1.5)
                view.tag = 1
            }
            contentView?.addSubview(view)
            viewArray.append(view)
        }
    }

3.添加旋转方法.

   ///contentView点击中心自动旋转方法
    @objc func autoRotateContentView(){
        ///先添加整体背景旋转动画
        let animation: POPBasicAnimation = POPBasicAnimation(propertyNamed: kPOPLayerRotation)
        ///旋转角度,给一个大的数字优化体验,防止卡顿
        animation.toValue = CGFloat.pi * 16
        ///动画持续时间
        animation.duration = 32
        ///使动画重复
        animation.repeatCount = Int(MAXPRI)
        ///动画线性匀速展示
        animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
        ///将动画添加到contentView.layer
        self.contentView?.layer.pop_add(animation, forKey: "rotation")

        ///添加中心View反向旋转动画
        let subAnimation: POPBasicAnimation = POPBasicAnimation(propertyNamed: kPOPLayerRotation)
        ///将旋转角度反向
        subAnimation.toValue = -CGFloat.pi * 16
        ///给一个相同速度来实现中心view不动
        subAnimation.duration = 32
        subAnimation.repeatCount = Int(MAXPRI)
        subAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
        ///将动画添加到circleView.layer
        self.circleView?.layer.pop_add(subAnimation, forKey: "subRotation")
        ///监听子view.frame的监听器暂停
        displayLink.isPaused = false
        ///给周围旋转的子View添加同样的反向旋转动画
        for subView in viewArray {
            subView.layer.pop_add(subAnimation, forKey: "subRotation")
        }
    }

4.修改最下方View的大小,并获取其image赋值给中心View

  /// 当subView在最下方时进行的修改其大小,修改中心circleView.image方法
    ///
    /// - Parameters:
    ///   - subView: subView
    ///   - bounds: 如果是长按动画进入的是subview.layer.presentation().bounds,手动滑动则是subView.bounds,需要根据bounds来                        计算subView在当前页面的frame
    func changeBottomSubView(subView: EWSubView, bounds: CGRect){
        ///获取view在当前页面的实际frame
        let rect = subView.convert(bounds, to: UIApplication.shared.keyWindow)
        let viewCenterX = rect.origin.x + (rect.width) / 2
        ///当View在页面中心线附近,并且处于下方时使用transform将其放大,并将中心imageView.image修改
        if viewCenterX > self.view.center.x - 20 && viewCenterX < self.view.center.x + 20 && rect.origin.y > (contentView?.center.y)! {
            if subView.tag == 0{
                subView.transform = subView.transform.scaledBy(x: 1.5, y: 1.5)
                ///使用tag标记确认其已被放大,防止重复放大,样式改变
                subView.tag = 1
                ///将放大的View提到最上方,保证展示效果
                subView.bringSubview(toFront: subView)
                self.circleView?.image = subView.imageView.image
            }
        }
        else {
            ///如果subView在变大状态
            if subView.tag == 1 {
                ///将不在条件中的View恢复原样
                subView.transform = subView.transform.scaledBy(x: 1/1.5, y: 1/1.5)
                subView.tag = 0
                contentView?.sendSubview(toBack: subView)
            }
        }
    }

demo地址:# EWCircleView-pop

通过touchMove获取手势,通过transform来实现类似效果的项目:Swift.轮转动画,100行代码搞定

有问题欢迎探讨.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容

  • 1 CALayer IOS SDK详解之CALayer(一) http://doc.okbase.net/Hell...
    Kevin_Junbaozi阅读 4,981评论 3 23
  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥iOS动画全貌。在这里你可以看...
    F麦子阅读 5,001评论 5 13
  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥ios动画全貌。在这里你可以看...
    每天刷两次牙阅读 8,321评论 6 30
  • 1、通过CocoaPods安装项目名称项目信息 AFNetworking网络请求组件 FMDB本地数据库组件 SD...
    X先生_未知数的X阅读 15,937评论 3 118
  • 没有去上班之前,我的大脑里是没有早教这个概念的,以为在孩子3岁的时候送她去幼儿园就行了,去上班之后才知道,在上幼儿...
    穆建园阅读 115评论 0 0