swift3 缩放悬浮窗的实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lin1109221208/article/details/80985435

需求:实现一个可缩放界面的悬浮窗,当界面缩小时,可以跟随手势滑动,并且可以继续操作其他界面

思路:将悬浮窗看成一个视图,视图上有一个缩放按钮,再将需要缩放的视图控制器的view加入悬浮窗视图,缩放按钮在界面放大时,可以自定义按钮的大小及位置,缩放按钮在界面缩小时,按钮大小与悬浮窗大小一致

实现效果如下


具体实现:

1、自定义悬浮窗视图

(1)重写UIView的init(frame:CGRect)方法,并添加缩放按钮及缩放界面,添加视图滑动的手势,计算视图初始展示的中心点及位置,将视图加入window,并展示在最上层

enum SuspendedBallLocation:Int {
        case SuspendedBallLocation_LeftTop = 0
        case SuspendedBallLocation_Top
        case SuspendedBallLocation_RightTop
        case SuspendedBallLocation_Right
        case SuspendedBallLocation_RightBottom
        case SuspendedBallLocation_Bottom
        case SuspendedBallLocation_LeftBottom
        case SuspendedBallLocation_Left
    }
    
    private var ballBtn:UIButton?
    private var timeLable:UILabel?
    private var currentCenter:CGPoint?
    private var panEndCenter:CGPoint = CGPoint.init(x: 0, y: 0)
    private var currentLocation:SuspendedBallLocation?

    
    var callingVC : ViewController!

override init(frame: CGRect) {
        super.init(frame: CGRect.init(x: 0, y: 0, width: frame.size.width, height: frame.size.height))
        
        
        //增加呼出界面
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        
        callingVC = (storyboard.instantiateViewController(withIdentifier: "VC")as?ViewController)!
        
        self.addSubview(callingVC.view)
        
        
        ballBtn = UIButton.init(type: .custom)
        ballBtn?.setBackgroundImage(UIImage.init(named: "shrink"), for: .normal)
        ballBtn?.imageView?.contentMode = .center
        ballBtn?.frame = CGRect.init(x: ScreenWidth-51, y: 40, width: 31, height: 23)
        
        ballBtn?.addTarget(self, action: #selector(clickBallViewAction), for: .touchUpInside)
        self.addSubview(self.ballBtn!)
        
        
        self.backgroundColor = UIColor.clear
        self.currentCenter = CGPoint.init(x: frame.size.width/2, y: frame.size.height/2) //初始位置
        self.calculateShowCenter(point: self.currentCenter!)
        self.configLocation(point: self.currentCenter!)
        //跟随手指拖动
        let moveGes:UIPanGestureRecognizer = UIPanGestureRecognizer.init(target: self, action: #selector(self.dragBallView))
        self.addGestureRecognizer(moveGes)
        
        //添加到window上
        self.ww_getKeyWindow().addSubview(self)
        //显示在视图的最上层
        self.ww_getKeyWindow().bringSubview(toFront: self)

    }
//MARK:- private utility
    func ww_getKeyWindow() -> UIWindow {
        if UIApplication.shared.keyWindow == nil {
            return ((UIApplication.shared.delegate?.window)!)!
        }else{
            return UIApplication.shared.keyWindow!
        }
    }
//计算浮窗展示的中心点
    func calculateShowCenter(point:CGPoint) {
        unowned let weakSelf = self
        UIView.animate(withDuration: 0.3) {
            weakSelf.center = CGPoint.init(x: point.x, y: point.y)
        }
    }
 //当前方位
    func configLocation(point:CGPoint) {
        if (point.x <= centerX*3 && point.y <= centerY*3) {
            self.currentLocation = .SuspendedBallLocation_LeftTop;
        }
        else if (point.x>centerX*3 && point.x<ScreenWidth-centerX*3 && point.y == centerY)
        {
            self.currentLocation = .SuspendedBallLocation_Top;
        }
        else if (point.x >= ScreenWidth-centerX*3 && point.y <= 3*centerY)
        {
            self.currentLocation = .SuspendedBallLocation_RightTop;
        }
        else if (point.x == ScreenWidth-centerX && point.y>3*centerY && point.y<ScreenHeight-centerY*3)
        {
            self.currentLocation = .SuspendedBallLocation_Right;
        }
        else if (point.x >= ScreenWidth-3*centerX && point.y >= ScreenHeight-3*centerY)
        {
            self.currentLocation = .SuspendedBallLocation_RightBottom;
        }
        else if (point.y == ScreenHeight-centerY && point.x > 3*centerX && point.x<ScreenWidth-3*centerX)
        {
            self.currentLocation = .SuspendedBallLocation_Bottom;
        }
        else if (point.x <= 3*centerX && point.y >= ScreenHeight-3*centerY)
        {
            self.currentLocation = .SuspendedBallLocation_LeftBottom;
        }
        else if (point.x == centerX && point.y > 3*centerY && point.y<ScreenHeight-3*centerY)
        {
            self.currentLocation = .SuspendedBallLocation_Left;
        }
    }

(2)缩放按钮点击方法:根据判断悬浮窗视图的width是否等于整个屏幕的width来决定视图执行的操作,若等于,则视图执行缩小操作,反之则放大

//MARK:- 悬浮窗按钮方法
    @objc func clickBallViewAction() {
        if self.frame.size.width == ScreenWidth {
            //缩小
            //固定缩放的中心点为右上角
            var frame = self.callingVC.view.frame
            self.layer.anchorPoint = CGPoint.init(x: 1, y: 0)
            self.frame = frame
            
            self.smallToShow()
            var callingVCFrame = self.callingVC.view.frame
            callingVCFrame.origin.y -= 70
            callingVCFrame.origin.x -= 50
            callingVCFrame.size.width += 100
            callingVCFrame.size.height += 140
            self.ballBtn?.frame = callingVCFrame
            ballBtn?.setBackgroundImage(UIImage.init(named: "拨打中"), for: .normal)
            ballBtn?.imageView?.contentMode = .scaleToFill
            
            
            
            
            
            let point = CGPoint.init(x: ScreenWidth-centerX, y:centerY) //初始位置
            self.calculateShowCenter(point: point)
            self.configLocation(point: point)
            
        }else{
            //放大
            
            
            //固定缩放的中心点为右上角
            let frame = CGRect.init(x: 0, y: 0, width: ScreenWidth, height: ScreenHeight)
            self.layer.anchorPoint = CGPoint.init(x: 1, y: 0)
            self.frame = frame
            
            self.bigToShow()
            self.frame = CGRect.init(x: 0, y: 0, width: ScreenWidth, height: ScreenHeight)
            self.ballBtn?.frame = CGRect.init(x: ScreenWidth-51, y: 40, width: 31, height: 23)
            ballBtn?.setBackgroundImage(UIImage.init(named: "shrink"), for: .normal)
            ballBtn?.imageView?.contentMode = .center
            
            
        }
    }
//放大
    func bigToShow(){
        let animation = CAKeyframeAnimation.init(keyPath: "transform")
        animation.duration = 0.5
        let values = NSMutableArray.init()
        values.add(NSValue.init(caTransform3D: CATransform3DMakeScale(0.2, 0.17, 1.0)))
        values.add(NSValue.init(caTransform3D: CATransform3DMakeScale(1.0, 1.0, 1.0)))
        animation.values = values as? [Any]
        
        self.layer.add(animation, forKey: nil)
        self.layer.transform = CATransform3DMakeScale(1, 1, 1)
    }
    
    //缩小
    func smallToShow(){
        let animation = CAKeyframeAnimation.init(keyPath: "transform")
        animation.duration = 0.5
        let values = NSMutableArray.init()
        values.add(NSValue.init(caTransform3D: CATransform3DMakeScale(1.0, 1.0, 1.0)))
        values.add(NSValue.init(caTransform3D: CATransform3DMakeScale(0.2, 0.17, 1.0)))
        animation.values = values as? [Any]
        
        self.layer.add(animation, forKey: nil)
        self.layer.transform = CATransform3DMakeScale(0.2, 0.17, 1)
    }
(3)滑动手势方法:根据手势最后停留的中心点计算悬浮窗停留的中心位置,中心点区域主要氛围9个部分,如图所示:


具体代码如下:

//跟随手指拖动
    @objc func dragBallView(panGes:UIPanGestureRecognizer) {
        if self.frame.size.width != ScreenWidth {
            let translation:CGPoint = panGes.translation(in: self.ww_getKeyWindow())
            let center:CGPoint = self.center
            self.center = CGPoint.init(x: center.x+translation.x, y: center.y+translation.y)
            panGes .setTranslation(CGPoint.init(x: 0, y: 0), in: self.ww_getKeyWindow())
            if panGes.state == UIGestureRecognizerState.ended{
                self.panEndCenter = self.center
                self.caculateBallCenter()
            }
        }
        
    }
    //计算中心位置
    func caculateBallCenter() {
        if (self.panEndCenter.x>centerX && self.panEndCenter.x < ScreenWidth-centerX && self.panEndCenter.y>centerY && self.panEndCenter.y<ScreenHeight-centerY) {
            //在上下左右距离边30的矩形里
            if (self.panEndCenter.y<3*centerY) {
                
                if self.panEndCenter.x < 4*centerX {
                    self.calculateBallNewCenter(point: CGPoint.init(x: 3*centerX , y: 30+centerY))
                }else{
                    self.calculateBallNewCenter(point: CGPoint.init(x: self.panEndCenter.x, y: 30+centerY))
                }
                
                
            }
            else if (self.panEndCenter.y>ScreenHeight-3*centerY)
            {
                //                左下角
                //
                if self.panEndCenter.x < 4*centerX {
                    self.calculateBallNewCenter(point: CGPoint.init(x: centerX+2*centerY, y: ScreenHeight-4*centerY-10))
                }else{
                    self.calculateBallNewCenter(point: CGPoint.init(x: self.panEndCenter.x, y: ScreenHeight-4*centerY-10))
                }
                
                
            }
            else
            {
                if (self.panEndCenter.x<=ScreenWidth/2) {
                     self.calculateBallNewCenter(point: CGPoint.init(x: centerX+2*centerY, y: self.panEndCenter.y))
                    
                }
                else{
                    self.calculateBallNewCenter(point: CGPoint.init(x: ScreenWidth-centerX, y: self.panEndCenter.y))
                }
            }
        }
        else
        {
            if (self.panEndCenter.x<=centerX && self.panEndCenter.y<=centerY)
            {
                self.calculateBallNewCenter(point: CGPoint.init(x: 3*centerX, y: centerY))
            }
            else if (self.panEndCenter.x>=ScreenWidth-centerX && self.panEndCenter.y<=centerY)
            {
                //右上角
                self.calculateBallNewCenter(point: CGPoint.init(x: ScreenWidth-centerX, y:centerY))
            }
            else if (self.panEndCenter.x>=ScreenWidth-centerX && self.panEndCenter.y>=ScreenHeight-centerY)
            {
                self.calculateBallNewCenter(point: CGPoint.init(x: ScreenWidth-centerX, y: ScreenHeight-centerY))
            }
            else if(self.panEndCenter.x<=centerX && self.panEndCenter.y>=ScreenHeight-centerY)
            {
                self.calculateBallNewCenter(point: CGPoint.init(x: centerX, y: ScreenHeight-centerY))
            }
            else if (self.panEndCenter.x>centerX && self.panEndCenter.x<ScreenWidth-centerX && self.panEndCenter.y<centerY)
            {
                //左上角
                if self.panEndCenter.x<4*centerX{
                    self.calculateBallNewCenter(point: CGPoint.init(x: 3*centerX, y: centerY))
                }else{
                    self.calculateBallNewCenter(point: CGPoint.init(x: self.panEndCenter.x, y: centerY))
                }
                
                
            }
            else if (self.panEndCenter.x>centerX && self.panEndCenter.x<ScreenWidth-centerX && self.panEndCenter.y>ScreenHeight-centerY)
            {
                self.calculateBallNewCenter(point: CGPoint.init(x: self.panEndCenter.x, y:ScreenHeight-centerY))
            }
            else if (self.panEndCenter.y>centerY && self.panEndCenter.y<ScreenHeight-centerY && self.panEndCenter.x<centerX)
            {
                self.calculateBallNewCenter(point: CGPoint.init(x: 3*centerX, y: self.panEndCenter.y))
            }
            else if (self.panEndCenter.y>centerY && self.panEndCenter.y<ScreenHeight-centerY && self.panEndCenter.x>ScreenWidth-centerX)
            {
                //右下角
                if self.panEndCenter.y>ScreenHeight-4*centerY {
                    self.calculateBallNewCenter(point: CGPoint.init(x: ScreenWidth-centerX, y:ScreenHeight-4*centerY-10))
                }else{
                    self.calculateBallNewCenter(point: CGPoint.init(x: ScreenWidth-centerX, y:self.panEndCenter.y))
                }
                
                
            }
        }
        
    }
//计算浮窗新的中心点
    func calculateBallNewCenter(point:CGPoint) {
        self.currentCenter = point
        self.configLocation(point: point)
        unowned let weakSelf = self
        UIView.animate(withDuration: 0.3) {
            weakSelf.center = CGPoint.init(x: point.x, y: point.y)
        }
    }

2、点击界面“点击弹出悬浮窗”按钮,弹出界面

整体框架如图所示


具体代码如下:

import UIKit

var ballView : IMOSuspendedBallView!

class FViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
         UserDefaults.standard.setValue("0", forKey: "ballViewSave")
        // Do any additional setup after loading the view.
    }
    @IBAction func click(_ sender: Any) {

        if UserDefaults.standard.object(forKey: "ballViewSave")as! String == "1" {
            let aleat = UIAlertController(title: "提示", message:"当前已有悬浮窗", preferredStyle: UIAlertControllerStyle.alert)
            let tempAction = UIAlertAction(title: "知道了", style: .cancel) { (action) in
                
            }
            aleat.addAction(tempAction)
            self.present(aleat, animated: true, completion: {
            })
        }else{
            ballView = IMOSuspendedBallView.init(frame: CGRect.init(x: 0, y: 0, width: ScreenWidth, height: ScreenHeight))
            UserDefaults.standard.setValue("1", forKey: "ballViewSave")
        }
        
    }
    
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    

    /*
    // MARK: - Navigation

    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        // Get the new view controller using segue.destinationViewController.
        // Pass the selected object to the new view controller.
    }
    */

}

3、弹出界面代码如下:

import UIKit


class ViewController: UIViewController {
    

    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = UIColor.lightGray
    }
    
    @IBAction func buttonClick(_ sender: Any) {
        ballView.dismissView()
    }
    
    
}

综上所述,具体代码实现:swift 实现可缩放悬浮窗

猜你喜欢

转载自blog.csdn.net/lin1109221208/article/details/80985435