Fix item list scrolling for touches outside section blocks

This commit is contained in:
Ilya Laktyushin 2020-07-27 18:26:36 +03:00
parent 2c52ee06de
commit 95b4d581cd
2 changed files with 67 additions and 49 deletions

View File

@ -184,6 +184,8 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
public final var keepMinimalScrollHeightWithTopInset: CGFloat? public final var keepMinimalScrollHeightWithTopInset: CGFloat?
public final var itemNodeHitTest: ((CGPoint) -> Bool)?
public final var stackFromBottom: Bool = false public final var stackFromBottom: Bool = false
public final var stackFromBottomInsetItemFactor: CGFloat = 0.0 public final var stackFromBottomInsetItemFactor: CGFloat = 0.0
public final var limitHitTestToNodes: Bool = false public final var limitHitTestToNodes: Bool = false
@ -3876,67 +3878,73 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
} }
self.touchesPosition = touchesPosition self.touchesPosition = touchesPosition
self.selectionTouchLocation = touches.first!.location(in: self.view)
self.selectionTouchDelayTimer?.invalidate() var processSelection = true
self.selectionLongTapDelayTimer?.invalidate() if let itemNodeHitTest = self.itemNodeHitTest, !itemNodeHitTest(touchesPosition) {
self.selectionLongTapDelayTimer = nil processSelection = false
let timer = Timer(timeInterval: 0.08, target: ListViewTimerProxy { [weak self] in }
if let strongSelf = self, strongSelf.selectionTouchLocation != nil {
strongSelf.clearHighlightAnimated(false)
if let index = strongSelf.itemIndexAtPoint(strongSelf.touchesPosition) { if processSelection {
var canBeSelectedOrLongTapped = false self.selectionTouchLocation = touches.first!.location(in: self.view)
for itemNode in strongSelf.itemNodes { self.selectionTouchDelayTimer?.invalidate()
if itemNode.index == index && (strongSelf.items[index].selectable && itemNode.canBeSelected) || itemNode.canBeLongTapped { self.selectionLongTapDelayTimer?.invalidate()
canBeSelectedOrLongTapped = true self.selectionLongTapDelayTimer = nil
} let timer = Timer(timeInterval: 0.08, target: ListViewTimerProxy { [weak self] in
} if let strongSelf = self, strongSelf.selectionTouchLocation != nil {
strongSelf.clearHighlightAnimated(false)
if canBeSelectedOrLongTapped { if let index = strongSelf.itemIndexAtPoint(strongSelf.touchesPosition) {
strongSelf.highlightedItemIndex = index var canBeSelectedOrLongTapped = false
for itemNode in strongSelf.itemNodes { for itemNode in strongSelf.itemNodes {
if itemNode.index == index && itemNode.canBeSelected { if itemNode.index == index && (strongSelf.items[index].selectable && itemNode.canBeSelected) || itemNode.canBeLongTapped {
if true { canBeSelectedOrLongTapped = true
if !itemNode.isLayerBacked { }
strongSelf.reorderItemNodeToFront(itemNode) }
for (_, headerNode) in strongSelf.itemHeaderNodes {
strongSelf.reorderHeaderNodeToFront(headerNode)
}
}
let itemNodeFrame = itemNode.frame
let itemNodeBounds = itemNode.bounds
if strongSelf.items[index].selectable {
itemNode.setHighlighted(true, at: strongSelf.touchesPosition.offsetBy(dx: -itemNodeFrame.minX + itemNodeBounds.minX, dy: -itemNodeFrame.minY + itemNodeBounds.minY), animated: false)
}
if itemNode.canBeLongTapped { if canBeSelectedOrLongTapped {
let timer = Timer(timeInterval: 0.3, target: ListViewTimerProxy { strongSelf.highlightedItemIndex = index
if let strongSelf = self, strongSelf.highlightedItemIndex == index { for itemNode in strongSelf.itemNodes {
for itemNode in strongSelf.itemNodes { if itemNode.index == index && itemNode.canBeSelected {
if itemNode.index == index && itemNode.canBeLongTapped { if true {
itemNode.longTapped() if !itemNode.isLayerBacked {
strongSelf.clearHighlightAnimated(true) strongSelf.reorderItemNodeToFront(itemNode)
strongSelf.selectionTouchLocation = nil for (_, headerNode) in strongSelf.itemHeaderNodes {
break strongSelf.reorderHeaderNodeToFront(headerNode)
}
}
let itemNodeFrame = itemNode.frame
let itemNodeBounds = itemNode.bounds
if strongSelf.items[index].selectable {
itemNode.setHighlighted(true, at: strongSelf.touchesPosition.offsetBy(dx: -itemNodeFrame.minX + itemNodeBounds.minX, dy: -itemNodeFrame.minY + itemNodeBounds.minY), animated: false)
}
if itemNode.canBeLongTapped {
let timer = Timer(timeInterval: 0.3, target: ListViewTimerProxy {
if let strongSelf = self, strongSelf.highlightedItemIndex == index {
for itemNode in strongSelf.itemNodes {
if itemNode.index == index && itemNode.canBeLongTapped {
itemNode.longTapped()
strongSelf.clearHighlightAnimated(true)
strongSelf.selectionTouchLocation = nil
break
}
} }
} }
} }, selector: #selector(ListViewTimerProxy.timerEvent), userInfo: nil, repeats: false)
}, selector: #selector(ListViewTimerProxy.timerEvent), userInfo: nil, repeats: false) strongSelf.selectionLongTapDelayTimer = timer
strongSelf.selectionLongTapDelayTimer = timer RunLoop.main.add(timer, forMode: RunLoop.Mode.common)
RunLoop.main.add(timer, forMode: RunLoop.Mode.common) }
} }
break
} }
break
} }
} }
} }
} }
} }, selector: #selector(ListViewTimerProxy.timerEvent), userInfo: nil, repeats: false)
}, selector: #selector(ListViewTimerProxy.timerEvent), userInfo: nil, repeats: false) self.selectionTouchDelayTimer = timer
self.selectionTouchDelayTimer = timer RunLoop.main.add(timer, forMode: RunLoop.Mode.common)
RunLoop.main.add(timer, forMode: RunLoop.Mode.common) }
super.touchesBegan(touches, with: event) super.touchesBegan(touches, with: event)
self.updateScroller(transition: .immediate) self.updateScroller(transition: .immediate)

View File

@ -238,7 +238,9 @@ open class ItemListControllerNode: ASDisplayNode, UIScrollViewDelegate {
self.listNode = ListView() self.listNode = ListView()
self.leftOverlayNode = ASDisplayNode() self.leftOverlayNode = ASDisplayNode()
self.leftOverlayNode.isUserInteractionEnabled = false
self.rightOverlayNode = ASDisplayNode() self.rightOverlayNode = ASDisplayNode()
self.rightOverlayNode.isUserInteractionEnabled = false
super.init() super.init()
@ -303,6 +305,14 @@ open class ItemListControllerNode: ASDisplayNode, UIScrollViewDelegate {
} }
} }
self.listNode.itemNodeHitTest = { [weak self] point in
if let strongSelf = self {
return point.x > strongSelf.leftOverlayNode.frame.maxX && point.x < strongSelf.rightOverlayNode.frame.minX
} else {
return true
}
}
let previousState = Atomic<ItemListNodeState?>(value: nil) let previousState = Atomic<ItemListNodeState?>(value: nil)
self.transitionDisposable.set(((state self.transitionDisposable.set(((state
|> map { presentationData, stateAndArguments -> ItemListNodeTransition in |> map { presentationData, stateAndArguments -> ItemListNodeTransition in