From e0e8ead9bac2bcf68033458edf4b11b41b67db2a Mon Sep 17 00:00:00 2001 From: booster Date: Wed, 13 May 2026 00:28:51 +0300 Subject: [PATCH] Tap blocking in GenericViewBlock commit_hash:ce1bc24cde851c2411491ac3972672ca8916989e --- .../LayoutKit/GenericViewBlock.swift | 37 +++++++++++---- ...nericViewBlock+UIViewRenderableBlock.swift | 46 ++++++++++++++++++- 2 files changed, 73 insertions(+), 10 deletions(-) diff --git a/client/ios/LayoutKit/LayoutKit/GenericViewBlock.swift b/client/ios/LayoutKit/LayoutKit/GenericViewBlock.swift index aedf4a403..4f04c61b3 100644 --- a/client/ios/LayoutKit/LayoutKit/GenericViewBlock.swift +++ b/client/ios/LayoutKit/LayoutKit/GenericViewBlock.swift @@ -37,6 +37,7 @@ public final class GenericViewBlock: BlockWithTraits { public let content: Lazy public let width: Trait public let height: Trait + public let isExclusiveTouch: Bool public var widthTrait: LayoutTrait { width.layoutTrait } public var heightTrait: LayoutTrait { height.layoutTrait } @@ -56,41 +57,57 @@ public final class GenericViewBlock: BlockWithTraits { public init( lazyContent: Lazy, width: Trait, - height: Trait + height: Trait, + isExclusiveTouch: Bool = false ) { self.content = lazyContent self.width = width self.height = height + self.isExclusiveTouch = isExclusiveTouch } public convenience init( content: Content, width: Trait, - height: Trait + height: Trait, + isExclusiveTouch: Bool = false ) { self.init( lazyContent: Lazy( onMainThreadGetter: { content } ), width: width, - height: height + height: height, + isExclusiveTouch: isExclusiveTouch ) } public convenience init( view: ViewType, width: CGFloat? = nil, - height: CGFloat? = nil + height: CGFloat? = nil, + isExclusiveTouch: Bool = false ) { - self.init(content: .view(view), width: width.trait, height: height.trait) + self.init( + content: .view(view), + width: width.trait, + height: height.trait, + isExclusiveTouch: isExclusiveTouch + ) } public convenience init( layer: LayerType, width: CGFloat? = nil, - height: CGFloat? = nil + height: CGFloat? = nil, + isExclusiveTouch: Bool = false ) { - self.init(content: .layer(layer), width: width.trait, height: height.trait) + self.init( + content: .layer(layer), + width: width.trait, + height: height.trait, + isExclusiveTouch: isExclusiveTouch + ) } public func intrinsicContentHeight(forWidth width: CGFloat) -> CGFloat { @@ -106,8 +123,10 @@ public final class GenericViewBlock: BlockWithTraits { return false } - return content.currentValue === other.content.currentValue && width == other - .width && height == other.height + return content.currentValue === other.content.currentValue + && width == other.width + && height == other.height + && isExclusiveTouch == other.isExclusiveTouch } public func getImageHolders() -> [ImageHolder] { [] } diff --git a/client/ios/LayoutKit/LayoutKit/UI/Blocks/GenericViewBlock+UIViewRenderableBlock.swift b/client/ios/LayoutKit/LayoutKit/UI/Blocks/GenericViewBlock+UIViewRenderableBlock.swift index 7b5fd7d09..d56f1b630 100644 --- a/client/ios/LayoutKit/LayoutKit/UI/Blocks/GenericViewBlock+UIViewRenderableBlock.swift +++ b/client/ios/LayoutKit/LayoutKit/UI/Blocks/GenericViewBlock+UIViewRenderableBlock.swift @@ -17,7 +17,9 @@ extension GenericViewBlock { overscrollDelegate _: ScrollDelegate?, renderingDelegate _: RenderingDelegate? ) { - (view as! GenericView).content = content.value + let genericView = view as! GenericView + genericView.content = content.value + genericView.exclusiveTouchEnabled = isExclusiveTouch } } @@ -34,8 +36,31 @@ private final class GenericView: UIView, BlockViewProtocol, VisibleBoundsTrackin } } + var exclusiveTouchEnabled = false { + didSet { + guard oldValue != exclusiveTouchEnabled else { return } + tapAbsorber.isEnabled = exclusiveTouchEnabled + } + } + + private let tapAbsorber = UITapGestureRecognizer() + var effectiveBackgroundColor: UIColor? { backgroundColor } + override init(frame: CGRect) { + super.init(frame: frame) + tapAbsorber.addTarget(self, action: #selector(absorbTap)) + tapAbsorber.delegate = self + tapAbsorber.cancelsTouchesInView = false + tapAbsorber.isEnabled = false + addGestureRecognizer(tapAbsorber) + } + + @available(*, unavailable) + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + override func layoutSubviews() { super.layoutSubviews() content?.setFrame(bounds) @@ -49,6 +74,25 @@ private final class GenericView: UIView, BlockViewProtocol, VisibleBoundsTrackin return result } } + + @objc private func absorbTap() {} +} + +extension GenericView: UIGestureRecognizerDelegate { + func gestureRecognizer( + _ gestureRecognizer: UIGestureRecognizer, + shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer + ) -> Bool { + guard gestureRecognizer === tapAbsorber else { return false } + guard let otherTap = otherGestureRecognizer as? UITapGestureRecognizer, + otherTap.numberOfTapsRequired == 1 + else { return false } + guard let otherView = otherTap.view, + otherView !== self, + isDescendant(of: otherView) + else { return false } + return true + } } extension GenericViewBlock.Content {