import UIKit extension RDReaderView { func tapCenter() { refreshToolViewsFromProviderIfNeeded() isShowToolView = !isShowToolView RDReaderTapDebug.log( "ReaderView.tapCenter", "toggle toolView visible=\(isShowToolView) top=\(RDReaderTapDebug.describe(topToolView)) bottom=\(RDReaderTapDebug.describe(bottomToolView))" ) 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 RDReaderTapDebug.log("ReaderView.tapCenter", "disabled collectionView/pageViewController interaction while toolView is visible") } 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 RDReaderTapDebug.log("ReaderView.tapCenter", "re-enabled collectionView/pageViewController interaction after hiding toolView") } onToolViewVisibilityChanged?(isShowToolView) } func isHitView(_ hitView: UIView?, inside toolView: UIView, point: CGPoint) -> Bool { if let hitView, hitView === toolView || hitView.isDescendant(of: toolView) { return true } if toolView.frame.contains(point) { // For search bar views, only consider the hit inside the visible // panel area. Taps on the dimmed scrim should pass through to // the reader content to allow chrome toggling. Touches that // resolved to a search bar subview (e.g. the accessibility-only // navigation buttons outside the panel) are already claimed by // the descendant check above. if let searchBarView = toolView as? RDEPUBReaderSearchBarView { let panelPoint = convert(point, to: searchBarView.panelView) if !searchBarView.panelView.point(inside: panelPoint, with: nil) { return false } } return true } return false } 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 ]) } } 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 } } }