微信里面阅读公众号或其他文章,经常需要暂时退出文章.
在新版微信中,可以把浏览的文章缩小为浮窗.点击浮窗继续阅读.对于经常在微信里阅读的人来说,这简直就是人类之光.
网上OC版本的模仿有很多,最近不是很忙,写了一个swift版本
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()
}
}
网友评论