// // RDReaderView+ToolView.swift // ReadViewSDK // // 文件职责:工具栏管理扩展,处理工具栏的显示/隐藏动画、安装和布局约束。 // import UIKit /// 工具栏管理扩展:处理工具栏的显示/隐藏动画、安装、布局 extension RDReaderView { /// 点击屏幕中央区域,切换工具栏的显示/隐藏,带动画效果 func tapCenter() { refreshToolViewsFromProviderIfNeeded() isShowToolView = !isShowToolView if isShowToolView { if let topToolView = topToolView { installToolViewIfNeeded(topToolView, position: .top) layoutIfNeeded() topToolView.transform = CGAffineTransform(translationX: 0, y: -topToolView.bounds.height) UIView.animate(withDuration: toolViewAnimationDuration) { topToolView.transform = .identity } } if let bottomToolView = bottomToolView { installToolViewIfNeeded(bottomToolView, position: .bottom) layoutIfNeeded() bottomToolView.transform = CGAffineTransform(translationX: 0, y: bottomToolView.bounds.height) UIView.animate(withDuration: toolViewAnimationDuration) { bottomToolView.transform = .identity } } collectionView.isUserInteractionEnabled = false pageViewController.view.isUserInteractionEnabled = false } else { if let topToolView = topToolView { UIView.animate(withDuration: toolViewAnimationDuration, animations: { topToolView.transform = CGAffineTransform(translationX: 0, y: -topToolView.bounds.height) }) { _ in topToolView.removeFromSuperview() } } if let bottomToolView = bottomToolView { UIView.animate(withDuration: toolViewAnimationDuration, animations: { bottomToolView.transform = CGAffineTransform(translationX: 0, y: bottomToolView.bounds.height) }) { _ in bottomToolView.removeFromSuperview() } } collectionView.isUserInteractionEnabled = true pageViewController.view.isUserInteractionEnabled = true } onToolViewVisibilityChanged?(isShowToolView) } /// 判断点击命中的视图是否在指定工具栏内,用于决定是否拦截点击事件 func isHitView(_ hitView: UIView?, inside toolView: UIView, point: CGPoint) -> Bool { if toolView.frame.contains(point) { return true } guard let hitView else { return false } return hitView === toolView || hitView.isDescendant(of: toolView) } /// 工具栏位置枚举 enum ToolViewPosition { /// 顶部工具栏(如标题栏、导航栏) case top /// 底部工具栏(如进度条、操作按钮) case bottom } /// 将工具栏安装到指定位置并设置布局约束,已安装则跳过 func installToolViewIfNeeded(_ toolView: UIView, position: ToolViewPosition) { guard toolView.superview !== self else { return } toolView.removeFromSuperview() addSubview(toolView) toolView.translatesAutoresizingMaskIntoConstraints = false let heightConstraint: NSLayoutConstraint switch position { case .top: topToolViewHeightConstraint?.isActive = false heightConstraint = toolView.heightAnchor.constraint(equalToConstant: resolvedToolViewHeight(for: .top)) topToolViewHeightConstraint = heightConstraint NSLayoutConstraint.activate([ toolView.leadingAnchor.constraint(equalTo: leadingAnchor), toolView.trailingAnchor.constraint(equalTo: trailingAnchor), toolView.topAnchor.constraint(equalTo: topAnchor), heightConstraint ]) case .bottom: bottomToolViewHeightConstraint?.isActive = false heightConstraint = toolView.heightAnchor.constraint(equalToConstant: resolvedToolViewHeight(for: .bottom)) bottomToolViewHeightConstraint = heightConstraint NSLayoutConstraint.activate([ toolView.leadingAnchor.constraint(equalTo: leadingAnchor), toolView.trailingAnchor.constraint(equalTo: trailingAnchor), toolView.bottomAnchor.constraint(equalTo: bottomAnchor), heightConstraint ]) } } /// 更新顶部和底部工具栏的高度约束,通常在 safeAreaInsets 变化时调用 func updateToolViewHeightConstraintsIfNeeded() { topToolViewHeightConstraint?.constant = resolvedToolViewHeight(for: .top) bottomToolViewHeightConstraint?.constant = resolvedToolViewHeight(for: .bottom) } private func resolvedToolViewHeight(for position: ToolViewPosition) -> CGFloat { let contentHeight: CGFloat = 52 switch position { case .top: return safeAreaInsets.top + contentHeight case .bottom: return safeAreaInsets.bottom + contentHeight } } }