328 lines
12 KiB
Swift
328 lines
12 KiB
Swift
//
|
|
// Example
|
|
// man
|
|
//
|
|
// Created by man 11/11/2018.
|
|
// Copyright © 2020 man. All rights reserved.
|
|
//
|
|
|
|
import UIKit
|
|
import UIKit.UIGestureRecognizerSubclass
|
|
|
|
protocol BubbleDelegate: AnyObject {
|
|
func didTapBubble()
|
|
}
|
|
|
|
//https://httpcodes.co/status/
|
|
private var _successStatusCodes = ["200","201","202","203","204","205","206","207","208","226"]
|
|
private var _informationalStatusCodes = ["100","101","102","103","122"]
|
|
private var _redirectionStatusCodes = ["300","301","302","303","304","305","306","307","308"]
|
|
|
|
private var _width: CGFloat = 25
|
|
private var _height: CGFloat = 25
|
|
|
|
class Bubble: UIView {
|
|
|
|
weak var delegate: BubbleDelegate?
|
|
|
|
public var width: CGFloat = _width
|
|
public var height: CGFloat = _height
|
|
|
|
private var numberLabel: UILabel? = {
|
|
return UILabel.init()
|
|
}()
|
|
private var networkNumber: Int = 0
|
|
|
|
|
|
static var originalPosition: CGPoint {
|
|
if CocoaDebugSettings.shared.bubbleFrameX != 0 && CocoaDebugSettings.shared.bubbleFrameY != 0 {
|
|
return CGPoint(x: CGFloat(CocoaDebugSettings.shared.bubbleFrameX), y: CGFloat(CocoaDebugSettings.shared.bubbleFrameY))
|
|
}
|
|
|
|
var h = 0
|
|
if #available(iOS 11.0, *) {
|
|
if UIApplication.shared.keyWindow?.safeAreaInsets.top ?? 0 > 24.0 {
|
|
h = 16;
|
|
}
|
|
}
|
|
return CGPoint(x: 1.875 + _width/2, y: UIScreen.main.bounds.size.height/2 - _height - CGFloat(h))
|
|
}
|
|
|
|
static var size: CGSize {return CGSize(width: _width, height: _height)}
|
|
|
|
|
|
//MARK: - tool
|
|
fileprivate func initLabelEvent(_ content: String, _ foo: Bool) {
|
|
if content == "🚀" || content == "❌"
|
|
{
|
|
//step 0
|
|
let WH: CGFloat = 20
|
|
//step 1
|
|
let label = UILabel()
|
|
label.text = content
|
|
label.font = UIFont.boldSystemFont(ofSize: 14)
|
|
|
|
//step 2
|
|
if foo == true {
|
|
label.frame = CGRect(x: self.frame.size.width/2 - WH/2, y: self.frame.size.height/2 - WH/2, width: WH, height: WH)
|
|
self.addSubview(label)
|
|
} else {
|
|
label.frame = CGRect(x: self.center.x - WH/2, y: self.center.y - WH/2, width: WH, height: WH)
|
|
self.superview?.addSubview(label)
|
|
}
|
|
//step 3
|
|
UIView.animate(withDuration: 0.8, animations: {
|
|
label.frame.origin.y = foo ? -100 : (self.center.y - 100)
|
|
label.alpha = 0
|
|
}, completion: { _ in
|
|
label.removeFromSuperview()
|
|
})
|
|
}
|
|
else
|
|
{
|
|
//step 0
|
|
let WH: CGFloat = 35
|
|
//step 1
|
|
let label = UILabel()
|
|
label.text = content
|
|
label.textAlignment = .center
|
|
label.adjustsFontSizeToFitWidth = true
|
|
label.font = UIFont.boldSystemFont(ofSize: 14)
|
|
|
|
if _informationalStatusCodes.contains(content) {
|
|
label.textColor = "#4b8af7".hexColor
|
|
}
|
|
else if _redirectionStatusCodes.contains(content) {
|
|
label.textColor = "#ff9800".hexColor
|
|
}
|
|
else {
|
|
label.textColor = .red
|
|
}
|
|
|
|
//step 3
|
|
if foo == true {
|
|
label.frame = CGRect(x: self.frame.size.width/2 - WH/2, y: self.frame.size.height/2 - WH/2, width: WH, height: WH)
|
|
self.addSubview(label)
|
|
} else {
|
|
label.frame = CGRect(x: self.center.x - WH/2, y: self.center.y - WH/2, width: WH, height: WH)
|
|
self.superview?.addSubview(label)
|
|
}
|
|
//step 4
|
|
UIView.animate(withDuration: 0.8, animations: {
|
|
label.frame.origin.y = foo ? -100 : (self.center.y - 100)
|
|
label.alpha = 0
|
|
}, completion: { _ in
|
|
label.removeFromSuperview()
|
|
})
|
|
}
|
|
}
|
|
|
|
|
|
fileprivate func initLayer() {
|
|
self.backgroundColor = .black
|
|
self.layer.cornerRadius = _width/2
|
|
self.sizeToFit()
|
|
|
|
if let numberLabel = numberLabel {
|
|
numberLabel.text = String(networkNumber)
|
|
numberLabel.textColor = .white
|
|
numberLabel.textAlignment = .center
|
|
numberLabel.adjustsFontSizeToFitWidth = true
|
|
numberLabel.isHidden = true
|
|
numberLabel.frame = CGRect(x:0, y:0, width:_width, height:_height)
|
|
self.addSubview(numberLabel)
|
|
}
|
|
|
|
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(Bubble.tap))
|
|
self.addGestureRecognizer(tapGesture)
|
|
|
|
let longTap = UILongPressGestureRecognizer.init(target: self, action: #selector(Bubble.longTap))
|
|
self.addGestureRecognizer(longTap)
|
|
}
|
|
|
|
func changeSideDisplay() {
|
|
UIView.animate(withDuration: 0.5, delay: 0.1, usingSpringWithDamping: 0.5,
|
|
initialSpringVelocity: 5, options: .curveEaseInOut, animations: {
|
|
}, completion: nil)
|
|
}
|
|
|
|
func updateOrientation(newSize: CGSize) {
|
|
let oldSize = CGSize(width: newSize.height, height: newSize.width)
|
|
let percent = center.y / oldSize.height * 100
|
|
let newOrigin = newSize.height * percent / 100
|
|
let originX = frame.origin.x < newSize.height / 2 ? _width/8*4.25 : newSize.width - _width/8*4.25
|
|
self.center = CGPoint(x: originX, y: newOrigin)
|
|
}
|
|
|
|
//MARK: - init
|
|
override init(frame: CGRect) {
|
|
super.init(frame: frame)
|
|
initLayer()
|
|
|
|
//添加手势
|
|
let selector = #selector(Bubble.panDidFire(panner:))
|
|
let panGesture = UIPanGestureRecognizer(target: self, action: selector)
|
|
self.addGestureRecognizer(panGesture)
|
|
|
|
//notification
|
|
NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: "reloadHttp_CocoaDebug"), object: nil, queue: OperationQueue.main) { [weak self] notification in
|
|
self?.reloadHttp_notification(notification)
|
|
}
|
|
|
|
NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: "deleteAllLogs_CocoaDebug"), object: nil, queue: OperationQueue.main) { [weak self] _ in
|
|
self?.deleteAllLogs_notification()
|
|
}
|
|
|
|
NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: "SHOW_COCOADEBUG_FORCE"), object: nil, queue: OperationQueue.main) { [weak self] _ in
|
|
self?.show_cocoadebug_force()
|
|
}
|
|
}
|
|
|
|
required init?(coder aDecoder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
deinit {
|
|
//notification
|
|
NotificationCenter.default.removeObserver(self)
|
|
}
|
|
|
|
//MARK: - notification
|
|
//网络通知
|
|
@objc func reloadHttp_notification(_ notification: Notification) {
|
|
|
|
guard let userInfo = notification.userInfo else {return}
|
|
let statusCode = userInfo["statusCode"] as? String
|
|
|
|
if _successStatusCodes.contains(statusCode ?? "") {
|
|
self.initLabelEvent("🚀", true)
|
|
}
|
|
else if statusCode == "0" { //"0" means network unavailable
|
|
self.initLabelEvent("❌", true)
|
|
}
|
|
else {
|
|
guard let statusCode = statusCode else {return}
|
|
self.initLabelEvent(statusCode, true)
|
|
}
|
|
|
|
|
|
self.networkNumber = (self.networkNumber ) + 1
|
|
self.numberLabel?.text = String(networkNumber)
|
|
|
|
|
|
if self.networkNumber == 0 {
|
|
self.numberLabel?.isHidden = true
|
|
} else {
|
|
self.numberLabel?.isHidden = false
|
|
}
|
|
|
|
if networkNumber >= 0 && networkNumber < 10 {
|
|
self.numberLabel?.font = UIFont.boldSystemFont(ofSize: 11)
|
|
} else if networkNumber >= 10 && networkNumber < 100 {
|
|
self.numberLabel?.font = UIFont.boldSystemFont(ofSize: 11)
|
|
} else if networkNumber >= 100 && networkNumber < 1000 {
|
|
self.numberLabel?.font = UIFont.boldSystemFont(ofSize: 9)
|
|
} else if networkNumber >= 1000 && networkNumber < 10000 {
|
|
self.numberLabel?.font = UIFont.boldSystemFont(ofSize: 7.5)
|
|
} else {
|
|
self.numberLabel?.font = UIFont.boldSystemFont(ofSize: 7)
|
|
}
|
|
}
|
|
|
|
|
|
@objc func deleteAllLogs_notification() {
|
|
self.networkNumber = 0
|
|
|
|
self.numberLabel?.text = String(networkNumber)
|
|
|
|
if self.networkNumber == 0 {
|
|
self.numberLabel?.isHidden = true
|
|
} else {
|
|
self.numberLabel?.isHidden = false
|
|
}
|
|
}
|
|
|
|
@objc func show_cocoadebug_force() {
|
|
CocoaDebugSettings.shared.showBubbleAndWindow = !CocoaDebugSettings.shared.showBubbleAndWindow
|
|
CocoaDebugSettings.shared.showBubbleAndWindow = !CocoaDebugSettings.shared.showBubbleAndWindow
|
|
}
|
|
|
|
|
|
//MARK: - target action
|
|
@objc func tap() {
|
|
delegate?.didTapBubble()
|
|
}
|
|
|
|
@objc func longTap() {
|
|
_HttpDatasource.shared().reset()
|
|
CocoaDebugSettings.shared.networkLastIndex = 0
|
|
NotificationCenter.default.post(name: NSNotification.Name("deleteAllLogs_CocoaDebug"), object: nil, userInfo: nil)
|
|
}
|
|
|
|
@objc func panDidFire(panner: UIPanGestureRecognizer) {
|
|
if panner.state == .began {
|
|
UIView.animate(withDuration: 0.5, delay: 0, options: .curveLinear, animations: { [weak self] in
|
|
self?.transform = CGAffineTransform(scaleX: 0.8, y: 0.8)
|
|
}, completion: nil)
|
|
}
|
|
|
|
let offset = panner.translation(in: self.superview)
|
|
panner.setTranslation(CGPoint.zero, in: self.superview)
|
|
var center = self.center
|
|
center.x += offset.x
|
|
center.y += offset.y
|
|
self.center = center
|
|
|
|
if panner.state == .ended || panner.state == .cancelled {
|
|
|
|
var frameInset: UIEdgeInsets
|
|
|
|
if #available(iOS 11.0, *) {
|
|
frameInset = UIApplication.shared.keyWindow?.safeAreaInsets ?? UIEdgeInsets(top: UIApplication.shared.statusBarFrame.height, left: 0, bottom: 0, right: 0)
|
|
} else {
|
|
frameInset = UIDevice.current.orientation.isPortrait ? UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 0) : .zero
|
|
}
|
|
|
|
let location = panner.location(in: self.superview)
|
|
let velocity = panner.velocity(in: self.superview)
|
|
|
|
var finalX: Double = Double(self.width/8*4.25)
|
|
var finalY: Double = Double(location.y)
|
|
|
|
if location.x > UIScreen.main.bounds.size.width / 2 {
|
|
finalX = Double(UIScreen.main.bounds.size.width) - Double(self.width/8*4.25)
|
|
}
|
|
|
|
self.changeSideDisplay()
|
|
|
|
let horizentalVelocity = abs(velocity.x)
|
|
let positionX = abs(finalX - Double(location.x))
|
|
|
|
let velocityForce = sqrt(pow(velocity.x, 2) * pow(velocity.y, 2))
|
|
|
|
let durationAnimation = (velocityForce > 1000.0) ? min(0.3, positionX / Double(horizentalVelocity)) : 0.3
|
|
|
|
if velocityForce > 1000.0 {
|
|
finalY += Double(velocity.y) * durationAnimation
|
|
}
|
|
|
|
if finalY > Double(UIScreen.main.bounds.size.height) - Double(self.height/8*4.25) {
|
|
finalY = Double(UIScreen.main.bounds.size.height) - Double(frameInset.bottom) - Double(self.height/8*4.25)
|
|
} else if finalY < Double(self.height/8*4.25) + Double(frameInset.top) {
|
|
finalY = Double(self.height/8*4.25) + Double(frameInset.top)
|
|
}
|
|
|
|
//
|
|
CocoaDebugSettings.shared.bubbleFrameX = Float(finalX)
|
|
CocoaDebugSettings.shared.bubbleFrameY = Float(finalY)
|
|
|
|
//
|
|
UIView.animate(withDuration: durationAnimation * 5, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 6, options: .allowUserInteraction, animations: { [weak self] in
|
|
self?.center = CGPoint(x: finalX, y: finalY)
|
|
self?.transform = CGAffineTransform.identity
|
|
}, completion:nil)
|
|
}
|
|
}
|
|
}
|
|
|