iOS点点滴滴iOS 开发每天分享优质文章视图控件

swift4.0 仿新版微信浮窗效果

2018-07-11  本文已影响14人  75cba68968ad

微信里面阅读公众号或其他文章,经常需要暂时退出文章.
在新版微信中,可以把浏览的文章缩小为浮窗.点击浮窗继续阅读.对于经常在微信里阅读的人来说,这简直就是人类之光.

网上OC版本的模仿有很多,最近不是很忙,写了一个swift版本
demo下载地址

效果预览.gif

demo下载地址

使用简单

// AppDelegate 中加入需要保持用来悬壶的控制器
FloatManager.addFloatVcClass(vcClass: ["XLSecondViewController"])

主要用到的技术

1、定时器、用来时时获取滑动返回的手势信息

//定时器
    lazy var link: CADisplayLink = {
        let obj = CADisplayLink.init(target: self, selector: #selector(panBack(currentLink:)))
        obj.add(to: RunLoop.main, forMode: .commonModes)
        obj.isPaused = true
        return obj
    }()

2、滑动返回兼听

extension FloatManager : UIGestureRecognizerDelegate {
    
    func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        if self.currentNavigationController().viewControllers.count > 1 {
            self.beginScreenEdgePanBack(ges: gestureRecognizer)
            
            return true
        }
        return false
    }
    
}

@objc func panBack(currentLink: CADisplayLink) {
        print("------")
        if self.edgePan?.state == UIGestureRecognizerState.changed {
            let tPoint = self.edgePan?.translation(in: kWindow)
            let x = max(UISCREEN_WIDTH + xFloatMargin - xCoef * (tPoint?.x)!, UISCREEN_WIDTH - xFloatAreaR)
            let y = max(UISCREEN_HEIGHT + xFloatMargin - xCoef * (tPoint?.x)!, UISCREEN_HEIGHT - xFloatAreaR)
            let rect = CGRect.init(x: x, y: y, width: xFloatAreaR, height: xFloatAreaR)
            self.floatArea.frame = rect
            let touchPoint = kWindow?.convert((self.edgePan?.location(in: kWindow))!, to: self.floatArea)
            print("touchPoint==\(touchPoint)")
            if ((touchPoint?.x)! > CGFloat(0) && (touchPoint?.y)! > CGFloat(0)) {
                
                if (pow(xFloatAreaR - (touchPoint?.x)!, 2) + pow(xFloatAreaR - (touchPoint?.y)!, 2) <= pow(xFloatAreaR, 2)) {
                    if (self.showFloatBall == false) {
                        self.showFloatBall = true
                    }
                } else {
                    if self.showFloatBall {
                        self.showFloatBall = false
                    }
                }
                
            } else {
                if self.showFloatBall {
                    self.showFloatBall = false
                }
            }
        } else if self.edgePan?.state == UIGestureRecognizerState.possible {
            UIView.animate(withDuration: 0.5, animations: {
                self.floatArea.frame = CGRect(x: UISCREEN_WIDTH, y: UISCREEN_HEIGHT, width: xFloatAreaR, height: xFloatAreaR)
            }) { (finished) in
                self.floatArea.removeFromSuperview()
                self.link.isPaused = true
                if self.showFloatBall {
                    self.floatVC = self.tempFloatVC
                    if self.haveIconImage() {
                        
                        let image = self.floatVC?.value(forKey: "xlIconImage")
                        if image != nil {
                            self.floatBall.imageView?.image = (image as! UIImage)
                        }
                    }
                    self.floatBall.frame = CGRect(x: UISCREEN_WIDTH - xBallSizeR - 15, y: UISCREEN_HEIGHT / 3, width: xBallSizeR, height: xBallSizeR)
                    self.floatBall.alpha = 1
                    self.kWindow?.addSubview(self.floatBall)
                }
                
            }
            
        }
        
    }

3、半圆状态

var highlight: Bool = false {
        
        didSet {
            
            if style == .XLFloatAreaViewStyle_default {
                self.title = highlight ? "释放开启浮窗" : "拖动到此,开启浮窗"
            } else if style == .XLFloatAreaViewStyle_cancel {
                self.title = highlight ? "释放关闭浮窗" : "拖动到此,关闭浮窗"
            }
            
            if highlight {
                if #available(iOS 10.0, *) {
                    let impactLight = UIImpactFeedbackGenerator.init(style: .medium)
                    impactLight.impactOccurred()
                } else {
                    // Fallback on earlier versions
                }
                
            }
            
            self.setNeedsDisplay()
            
        }
        
    }

4、悬浮按钮的事件处理

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        
        for obj in touches {
            
            self.center = obj.location(in: UIApplication.shared.keyWindow)
            if self.delegate != nil {
                self.delegate?.floatBallBeginMove()
            }
            
        }
        
    }
    
    override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
        
        self.endTouch(touces: touches)
        
    }
    
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        
        self.endTouch(touces: touches)

    }
    
    //手势结束时对视图位置重新设定
    func endTouch(touces: Set<UITouch>) {
        var frame = self.frame
        let navHeight: CGFloat = UIApplication.shared.statusBarFrame.height + 44
        for obj in touces {
            let point = obj.location(in: UIApplication.shared.keyWindow)
            if point.x > (UISCREEN_WIDTH / 2) {
                frame.origin.x = UISCREEN_WIDTH - frame.size.width - margin
            } else {
                frame.origin.x = margin
            }
            
            if frame.origin.y > (UISCREEN_HEIGHT - frame.size.height - margin) {
                frame.origin.y = UISCREEN_HEIGHT - frame.size.height - margin
            } else if frame.origin.y < navHeight {
                frame.origin.y = navHeight
            }
            
            UIView.animate(withDuration: 0.3) {
                self.frame = frame
            }
        }
        
        if self.delegate != nil {
            self.delegate?.floatBallEndMove()
        }
        
    }
    
     @objc private func ballClick() {
        
        if self.delegate != nil {
            self.delegate?.floatBallClick()
        }
    }

5、push、pop动画

push

extension XLTransitionPush: UIViewControllerAnimatedTransitioning {
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return xTimeInterval
    }
    
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        self.transitionContext = transitionContext
        
        let fromVC = transitionContext.viewController(forKey: .from)
        let toVC = transitionContext.viewController(forKey: .to)
        
        let contView = transitionContext.containerView
        contView.addSubview((fromVC?.view)!)
        contView.addSubview((toVC?.view)!)
        
        let floatBallRect = FloatManager.shareFloatManager.floatBall.frame
        fromVC?.view.addSubview(self.coverView)
        
        let startBezierPath = UIBezierPath.init(roundedRect: CGRect(x: floatBallRect.origin.x, y: floatBallRect.origin.y, width: floatBallRect.size.width, height: floatBallRect.size.height), cornerRadius: floatBallRect.size.width / 2)
        let finishBezierPath = UIBezierPath.init(roundedRect: CGRect(x: 0, y: 0, width: UISCREEN_WIDTH, height: UISCREEN_HEIGHT), cornerRadius: floatBallRect.size.width / 2)
        
        let layer = CAShapeLayer()
        layer.path = startBezierPath.cgPath
        toVC?.view.layer.mask = layer
        
        let layerAnimation = CABasicAnimation(keyPath: "path")
        layerAnimation.fromValue = startBezierPath.cgPath
        layerAnimation.toValue = finishBezierPath.cgPath
        layerAnimation.duration = xTimeInterval
        layerAnimation.timingFunction = CAMediaTimingFunction.init(name: kCAMediaTimingFunctionEaseInEaseOut)
        layerAnimation.delegate = self
        layer.add(layerAnimation, forKey: "path")
        
        UIView.animate(withDuration: xTimeInterval) {
            FloatManager.shareFloatManager.floatBall.alpha = 0
        }
    }

    
}

extension XLTransitionPush: CAAnimationDelegate {
    
    func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
        self.transitionContext?.completeTransition(true)
        self.transitionContext?.viewController(forKey: .from)?.view.layer.mask = nil
        self.transitionContext?.viewController(forKey: .to)?.view.layer.mask = nil
        self.coverView.removeFromSuperview()
    }
    
}

pop

extension XLTransitionPop: UIViewControllerAnimatedTransitioning {
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return xTimeInterval
    }
    
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        self.transitionContext = transitionContext
        
        let fromVC = transitionContext.viewController(forKey: .from)
        let toVC = transitionContext.viewController(forKey: .to)
        
        let contView = transitionContext.containerView
        contView.addSubview((toVC?.view)!)
        contView.addSubview((fromVC?.view)!)
        
        let floatBallRect = FloatManager.shareFloatManager.floatBall.frame
        toVC?.view.addSubview(self.coverView)
        
        let startBezierPath = UIBezierPath.init(roundedRect: CGRect(x: floatBallRect.origin.x, y: floatBallRect.origin.y, width: floatBallRect.size.width, height: floatBallRect.size.height), cornerRadius: floatBallRect.size.width / 2)
        let finishBezierPath = UIBezierPath.init(roundedRect: CGRect(x: 0, y: 0, width: UISCREEN_WIDTH, height: UISCREEN_HEIGHT), cornerRadius: floatBallRect.size.width / 2)
        
        let layer = CAShapeLayer()
        layer.path = startBezierPath.cgPath
        fromVC?.view.layer.mask = layer
        
        let layerAnimation = CABasicAnimation(keyPath: "path")
        layerAnimation.toValue = startBezierPath.cgPath
        layerAnimation.fromValue = finishBezierPath.cgPath
        layerAnimation.duration = xTimeInterval
        layerAnimation.timingFunction = CAMediaTimingFunction.init(name: kCAMediaTimingFunctionEaseInEaseOut)
        layerAnimation.delegate = self
        layer.add(layerAnimation, forKey: "path")
        
        UIView.animate(withDuration: xTimeInterval) {
            self.coverView.alpha = 0
            FloatManager.shareFloatManager.floatBall.alpha = 1
        }
    }
    
    
}

extension XLTransitionPop: CAAnimationDelegate {
    
    func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
        self.transitionContext?.completeTransition(true)
        self.transitionContext?.viewController(forKey: .from)?.view.layer.mask = nil
        self.transitionContext?.viewController(forKey: .to)?.view.layer.mask = nil
        self.coverView.removeFromSuperview()
    }
    
}

demo下载地址

以上便是实现该效果的全部实现.上方含有部分伪代码,全部代码已上传至 Github下载地址 欢迎(跪求) Star.

上一篇 下一篇

猜你喜欢

热点阅读