相信大家都有用过了,业余时间模仿了一下
-
浮窗的移动、隐藏
显示、移动、移除部分
正文
思想:把整个功能拆分为三个部分:
- 1、浮窗按钮:
UIButton
这个按钮为浮窗功能的核心,主要包括: 通过touch
系列事件处理移动浮窗、进入二级页面、隐藏浮窗- 2、取消浮窗:
UIView
这个View就是用来显示交互的,构成为一个Button
和CAShapeLayer
- 3、一个
Controller
用于显示内容
这个就是内容的显示,其中包括了:自定义转场动画、浮窗的显示/隐藏逻辑。
一、 Button
的实现
主要就是监听三个方法:touchBegan
,touchesMoved
,touchesEnded
-
touchesMoved
①:中核心操作就是把当前的点转化为在父视图中的点
guard let touch = touches.first, let superView = self.superview else { return }
let point = touch.location(in: superView)
self.frame.origin.x = point.x - frame.width * 0.5
self.frame.origin.y = point.y - frame.height * 0.5
②:隐藏浮窗View的显示
// show hide floating window view
UIView.animate(withDuration: 0.2) {
hideWindowView.frame.origin = CGPoint(x: UIScreen.main.bounds.width - hideWindowView.frame.width, y: UIScreen.main.bounds.height - hideWindowView.frame.height)
hideWindowView.moreRedBackground(isHiddenBtn)
}
-
touchesEnded 中核心操作
①:修改按钮的x坐标
let currentPointX = frame.origin.x + frame.width * 0.5
let screenW = UIScreen.main.bounds.width
let pointX = (currentPointX > screenW * 0.5) ? (screenW - frame.width) - margin : margin
②:判断是否移动了,如果没移动就触发进入浮窗方法
guard let touch = touches.first, let superView = superview else { return }
if lastPoint == touch.location(in: superView) { //push
let controller = UIApplication.shared.keyWindow?.rootViewController
var nav = controller?.navigationController
if nav == nil {
nav = controller?.childViewControllers.first?.navigationController
}
nav?.pushViewController(fwController, animated: true)
return
}
③:隐藏浮窗和浮窗按钮的显示与隐藏
// show hide floating window view
UIView.animate(withDuration: 0.2) {
hideWindowView.frame.origin = CGPoint(x: UIScreen.main.bounds.width, y: UIScreen.main.bounds.height)
self.isHidden = isHiddenBtn
}
二、隐藏浮窗View的实现
①:Button 通过titleEdgeInsets
和imageEdgeInsets
来自定义布局
//自定义按钮
class PQButton: UIButton {
convenience init(_ textLayout: EIQMenuTextLayout){
self.init(frame: .zero)
self.textLayout = textLayout
}
enum EIQMenuTextLayout: Int {
case top,left,bottom,right
}
public var spacing: CGFloat = 20
public var textLayout: EIQMenuTextLayout = .right
override func layoutSubviews() {
super.layoutSubviews()
guard let title = self.titleLabel?.text as NSString?, let titleFont = self.titleLabel?.font else { return }
let imageSize = self.imageRect(forContentRect: self.frame)
let titleSize = title.size(withAttributes: [NSAttributedStringKey.font : titleFont])
var titleInsets: UIEdgeInsets = self.titleEdgeInsets
var imageInsets: UIEdgeInsets = self.imageEdgeInsets
switch textLayout {
case .left:
titleInsets = UIEdgeInsets(top: 0, left: -(imageSize.width * 2), bottom: 0, right: 0)
imageInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0,
right: -(titleSize.width * 2 + spacing))
case .top:
titleInsets = UIEdgeInsets(top: -(imageSize.height + titleSize.height + spacing),
left: -(imageSize.width), bottom: 0, right: 0)
imageInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: -titleSize.width)
case .bottom:
titleInsets = UIEdgeInsets(top: (imageSize.height + titleSize.height + spacing),
left: -(imageSize.width), bottom: 0, right: 0)
imageInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: -titleSize.width)
default:
break
}
self.titleEdgeInsets = titleInsets
self.imageEdgeInsets = imageInsets
}
}
②:通过view.layer.mask
去实现
创建CAShapeLayer
private lazy var maskLayer: CAShapeLayer = {
let layer = CAShapeLayer()
layer.frame = self.bounds
self.layer.mask = layer
return layer
}()
设置path
public func moreRedBackground(_ more: Bool){
var radius = frame.width
if more {
radius += 5
}
let path = UIBezierPath(arcCenter: CGPoint(x: frame.width + 10, y: frame.height + 10), radius: radius, startAngle: 0, endAngle: CGFloat(Double.pi * 2), clockwise: false)
maskLayer.path = path.cgPath
}
三、Controller的实现
只是实现Controller就没什么好说的,这里主要说的是转场动画部分
- 第一步:实现代理
navigationController?.delegate = self
- 第二步:实现代理协议方法
这个代理方法中如果返回了 nil ,即表示使用系统自带的pop、push效果
func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
if operation == .push {
return TransitionAnimator(isPush: true, animation: .overlay)
}
if operation == .pop && !fwBtnIsHidden {
return TransitionAnimator(isPush: false, animation: .overlay)
}
return nil
}
ps:如果想实现交互式处理需要实现另外一个方法, 在该方法中返回处理对象
@available(iOS 7.0, *)
optional public func navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning?
UIViewControllerAnimatedTransitioning
协议介绍:
返回动画时长
public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval
动画具体处理 containerView为内容视图,所有显示的内容均要添加进去方可显示
public func animateTransition(using transitionContext: UIViewControllerContextTransitioning)
网友评论