一个添加在window上悬浮视图。可固定,可拖动,还可以限制一个范围内拖动,可限定在只某些页面展示。
主要是用来解决项目中弹出的悬浮广告,类似悬浮球。
Demo:
https://github.com/Gamin-fzym/GASuspendViewDemo
https://download.csdn.net/download/u012881779/87614648
实现:
悬浮视图和视图配置
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
}
悬浮视图管理
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
}
}
}
示意图:
1.限制悬浮视图只能在一个范围内拖动
2.限制悬浮视图只能靠右上下拖动
3.限制悬浮视图只能靠底部左右拖动