美文网首页Swift工作笔记
Swift.轮转动画,100行代码搞定

Swift.轮转动画,100行代码搞定

作者: 王四猫 | 来源:发表于2018-08-20 14:44 被阅读102次
    效果图

    实现效果:

    静止时:子view对称排列,允许动态添加,0~24个都能较好的显示.

    旋转时:中心view不动,子view随手势旋转,最下方子view变大突出.

    实现思路:

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

    最下方的view变大是循环判断子view.frame.x,当它处于一个范围,并且frame.y大于中心view.frame.y的时候.修改它的transform,来使其变大,并且修改它的tag来标记它已经属于变大状态,当它frame.x超出了预定范围,使其还原.

    实现方式:

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

    2.添加周围旋转子view.

    3.添加旋转方法.

    4.交互优化.


    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.添加旋转方法.

     /// 核心旋转方法,具体办法是背景view旋转,中心view和子view同角度反向旋转,实现动画效果
        override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
            guard let contentView = contentView else { return }
            guard let circleView = circleView else { return }
            orgin = CGPoint(x: 0.5 * ScreenInfo.Width, y: MENURADIUS + 0.17 * ScreenInfo.Height)
            let currentPoint = touches.first?.location(in: self.view)
            if self.touchPointInsideCircle(center: orgin!, radius: MENURADIUS*1.45, targetPoint: currentPoint!){
                b = DIST(pointA: beginPoint!, pointB: orgin!)
                c = DIST(pointA: currentPoint!, pointB: orgin!)
                a = DIST(pointA: beginPoint!, pointB: orgin!)
                let angleBegin = atan2(beginPoint!.y - orgin!.y, beginPoint!.x - orgin!.x)
                let angleAfter = atan2(currentPoint!.y - orgin!.y, currentPoint!.x - orgin!.x)
                let angle = angleAfter - angleBegin
                // 背景view旋转
                contentView.transform = contentView.transform.rotated(by: angle)
                // 中心view反向旋转
                circleView.transform = circleView.transform.rotated(by: -angle)
                for i in 0..<viewArray.count {
                    // 子view反向旋转
                    viewArray[i].transform = viewArray[i].transform.rotated(by: -angle)
                    // 判断实现最下方的子view变大
                    let rect = viewArray[i].convert(viewArray[i].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 {
                        if viewArray[i].tag == 0{
                            viewArray[i].transform = viewArray[i].transform.scaledBy(x: 1.5, y: 1.5)
                            viewArray[i].tag = 1
                            contentView.bringSubview(toFront: viewArray[i])
                        }
                    }
                    else {
                        if viewArray[i].tag == 1 {
                            viewArray[i].transform = viewArray[i].transform.scaledBy(x: 1/1.5, y: 1/1.5)
                            viewArray[i].tag = 0
                            contentView.sendSubview(toBack: viewArray[i])
                        }
                    }
                }
                beginPoint = currentPoint
            }
        }
    

    4.交互优化.

         /// 获取手指触摸位置,超过范围不让旋转交互
        private func touchPointInsideCircle(center: CGPoint, radius: CGFloat, targetPoint: CGPoint) -> Bool{
            let dist = DIST(pointA: targetPoint, pointB: center)
            return (dist <= radius)
        }
    
        func DIST(pointA: CGPoint, pointB: CGPoint) -> CGFloat{
            let x = (pointA.x - pointB.x) * (pointA.x - pointB.x)
            let y = (pointA.y - pointB.y) * (pointA.y - pointB.y)
            return CGFloat(sqrtf(Float(x + y)))
       }
    

    demo地址:https://github.com/WangLiquan/circleView.求star

    有问题欢迎探讨.

    相关文章

      网友评论

        本文标题:Swift.轮转动画,100行代码搞定

        本文链接:https://www.haomeiwen.com/subject/sofliftx.html