[Swift] Add a draggable floating view on the window

One adds a floating view on the window. It can be fixed, dragged, and dragged within a certain range, and can be limited to only certain pages.

It is mainly used to solve the floating advertisements that pop up in the project, similar to floating balls.

Demo:

https://github.com/Gamin-fzym/GASuspendViewDemo

https://download.csdn.net/download/u012881779/87614648

accomplish:

Floating Views and View Configurations

import Foundation
import UIKit
import Kingfisher

class GASuspendView: UIView {
    
    lazy var thumbIV : UIImageView = {
        let iv = UIImageView()
        iv.backgroundColor = .clear
        iv.contentMode = .scaleAspectFill
        iv.isUserInteractionEnabled = false
        return iv
    }()
    var vProperty: GASuspendViewProperty? {
        didSet {
            guard let vo = vProperty else { return }
            self.frame = CGRectMake(vo.startPoint.x, vo.startPoint.y, vo.width, vo.height)
            self.layer.cornerRadius = vo.corner
            self.layer.masksToBounds = true
            thumbIV.frame = self.bounds
        }
    }
    var clickedBlock: ((GASuspendModel?) -> ())?
    var imageLoadFinish: (() -> ())?
    var model: GASuspendModel? {
        didSet {
            guard let vo = model else { return }
            thumbIV.kf.setImage(with: URL(string: vo.thumbPath)) { [weak self] result in
                DispatchQueue.main.async {
                    switch result {
                    case .success(let value):
                        let _ = value.image
                        self?.imageLoadFinish?()
                        break
                    case .failure(_):
                        break
                    }
                }
            }
        }
    }
    
    deinit {
        clickedBlock = nil
        imageLoadFinish = nil
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        self.addSubview(thumbIV)
        let tap = UITapGestureRecognizer(target: self, action: #selector(tapAction(gesture:)))
        self.addGestureRecognizer(tap)
        let pan = UIPanGestureRecognizer(target: self, action: #selector(panAction(gesture:)))
        self.addGestureRecognizer(pan)
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError()
    }
    
    @objc func tapAction(gesture: UITapGestureRecognizer) {
        clickedBlock?(model)
    }
    
    @objc func panAction(gesture: UIPanGestureRecognizer) {
        guard let window = GASuspendManager.appCurrentWindow() else { return }
        window.bringSubviewToFront(self)
        let translation = gesture.translation(in: window)
        var center = CGPoint(x: self.center.x + translation.x, y: self.center.y + translation.y)
        if let limitBounds = vProperty?.limitBounds, let width = vProperty?.width, let height = vProperty?.height {
            let limitCenterRect = CGRect(x: limitBounds.origin.x + width/2.0,
                                         y: limitBounds.origin.y + height/2.0,
                                         width: limitBounds.size.width - width,
                                         height: limitBounds.size.height - height)
            if !limitCenterRect.contains(center) {
                if center.x < limitCenterRect.origin.x {
                    center.x = limitCenterRect.origin.x
                }
                if center.x > limitCenterRect.origin.x + limitCenterRect.size.width {
                    center.x = limitCenterRect.origin.x + limitCenterRect.size.width
                }
                if center.y < limitCenterRect.origin.y {
                    center.y = limitCenterRect.origin.y
                }
                if center.y > limitCenterRect.origin.y + limitCenterRect.size.height {
                    center.y = limitCenterRect.origin.y + limitCenterRect.size.height
                }
            }
        }
        self.center = center
        gesture.setTranslation(CGPoint.zero, in: window)
    }
    
}

// MARK: - 浮窗的相关配置属性
class GASuspendViewProperty: NSObject {
    
    /// 限制滑动区域
    var limitBounds: CGRect = UIScreen.main.bounds
    /// 浮窗宽度
    var width: CGFloat = 81
    /// 浮窗高度
    var height: CGFloat = 168
    /// 开始位置
    var startPoint: CGPoint = CGPoint(x: 0, y: 0)
    /// 圆角
    var corner: CGFloat = 0
    /// 边距
    var padding: CGFloat = 10

}

floating view management

import Foundation
import UIKit

class GASuspendManager {
    
    static let shared = GASuspendManager()
    private var suspendModel: GASuspendModel?

    lazy var suspendView: GASuspendView = {
        let view = GASuspendView(frame: .zero)
        view.tag = 88888
        view.imageLoadFinish = {
            view.isHidden = false
        }
        view.clickedBlock = { [weak self] model in
            print("tap suspendView")
        }
        view.isHidden = true
        return view
    }()
    
    func setSuspendData() {
        setSuspendData(completed: {_ in })
    }
    
    func setSuspendData(completed: @escaping ((GASuspendModel?) -> ())) {
        // request data
        let model = GASuspendModel()
        setSuspendData(model)
        completed(model)
        GASuspendManager.updateSuspendViewShow()
    }
    
    func setSuspendData(_ model: GASuspendModel) {
        suspendModel = model
        setupSuspendView(model)
    }
    
    func setupSuspendView(_ model: GASuspendModel) {
        let vProperty = GASuspendViewProperty()
        vProperty.padding = model.padding
        vProperty.width = model.width
        vProperty.height = model.height
        vProperty.corner = model.corner
        
        // 限制在一个范围内拖动
        vProperty.limitBounds = CGRectMake(vProperty.padding,
                                           navigationBarHeight() + vProperty.padding,
                                           screenWidth()-vProperty.padding*2,
                                           screenHeight() - navigationBarHeight() - tabbarHeight() - 2*vProperty.padding)
        /*
        // 限制只能挨着右侧边框上下拖动
        vProperty.limitBounds = CGRectMake(screenWidth() - vProperty.padding - vProperty.width,
                                           navigationBarHeight() + vProperty.padding,
                                           vProperty.width,
                                           screenHeight() - navigationBarHeight() - tabbarHeight() - 2*vProperty.padding)
         */
        /*
        // 限制只能挨着底部边框左右拖动
        vProperty.limitBounds = CGRectMake(vProperty.padding,
                                           screenHeight() - vProperty.padding - vProperty.height - tabbarHeight(),
                                           screenWidth() - 2*vProperty.padding,
                                           vProperty.height)
         */
                 
        vProperty.startPoint = CGPoint(x: vProperty.limitBounds.origin.x + vProperty.limitBounds.size.width - vProperty.width ,
                                       y: vProperty.limitBounds.origin.y + vProperty.limitBounds.size.height - vProperty.height)
        vProperty.corner = model.corner
        suspendView.vProperty = vProperty
        suspendView.isHidden = true
        suspendView.model = model
        GASuspendManager.appCurrentWindow()?.addSubview(suspendView)
    }
    
    /// 从window获取SuspendView
    func getSuspendViewInWindow() -> GASuspendView? {
        if let view = GASuspendManager.appCurrentWindow()?.viewWithTag(88888) as? GASuspendView {
            return view
        }
        return nil
    }
    
    /// 更新悬浮视图状态
    static func updateSuspendViewShow() {
        let limitVCArr: [String] = ["ViewController", "HomeVC"]
        if let topVC = GASuspendManager.topViewController(), limitVCArr.contains(topVC.className) {
            if GASuspendManager.shared.suspendModel == nil {
                GASuspendManager.shared.getSuspendViewInWindow()?.isHidden = true
            } else {
                GASuspendManager.shared.getSuspendViewInWindow()?.isHidden = false
                if let view = GASuspendManager.shared.getSuspendViewInWindow() {
                    GASuspendManager.appCurrentWindow()?.bringSubviewToFront(view)
                }
            }
        } else {
            GASuspendManager.shared.getSuspendViewInWindow()?.isHidden = true
        }
    }
    
}

Schematic diagram:

1. Restrict the floating view to only be dragged within a range

2. The floating view can only be dragged up and down from the right 

 3. Restrict the floating view to drag left and right at the bottom

 

 

Guess you like

Origin blog.csdn.net/u012881779/article/details/129781308