Post

Telegram研究:ListView/ItemListRevealOptionsItemNode侧滑删除

Telegram:
仓库:https://github.com/TelegramMessenger/Telegram-iOS
tag: release-10.1
主要基类及协议:ListViewListViewInsertItemListViewItemItemListRevealOptionsItemNode
主要学习源码文件:CallListViewTransition.swiftCallListControllerNode.swiftCallListCallItem.swift\

本例Demo: 仓库: https://github.com/h42330789/StudyIM/tree/feature/ListView/StudyAsynDisplay
分支:origin/feature/ItemListController
主要Demo文件:MySwipeDeleteListVC.swift

前言

Telegram包含了很多库,其中与UI有关的最核心的是AsyncDisplayKitDisplay,其中UI库里使用最广的又是ListView,在列表里还有不少侧滑删除的功能

一、 1、要实现列表里展示测试的功能,itemNode要继承ItemListRevealOptionsItemNode

1
2
3
class CallListCallItemNode: ItemListRevealOptionsItemNode {
    // ...
}

2、设置滑动项setRevealOptions(left: [xx,xx], right:[xx,xx])
活动内容的具体子内容:ItemListRevealOptionsNode -> ItemListRevealOptionNode -> ASTextNode + ASImageNode ,字体大小为固定的Font.regular(17)或Font.medium(13)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class CallListCallItemNode: ItemListRevealOptionsItemNode {
    // ...
    func asyncLayout() -> xxxx {
        // ...
        return { [weak self] .... in
            return (node, {[weak self] synchronousLoads in
                return (xxx, { [weak self] in 
                    // ...
                    // 设置左右可选项
                    self?.setRevealOptions((left: [], right: [ItemListRevealOption(key: 0, title: "删除", icon: .none, color: .green, textColor: .red)]))
                    // 设置初始化数据
                    self?.setRevealOptionsOpened(item.revealed, animated: animated)
                })
             })
        }
    }
}

3、子类处理滑动过程中的回调updateRevealOffset(offset: CGFloat, transition: ContainedViewLayoutTransition)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class CallListCallItemNode: ItemListRevealOptionsItemNode {
    // 侧滑功能滑动回调
    override func updateRevealOffset(offset: CGFloat, transition: ContainedViewLayoutTransition) {
        // ...
        // 重新计算每个组件的x,offset是总体移动的位置
        var avatarFrame = self.avatarNode.frame
        avatarFrame.origin.x = revealOffset + leftInset - 52.0
        transition.updateFrameAdditive(node: self.avatarNode, frame: avatarFrame)
        // ...
    }
    // 按钮展示回调
    override func revealOptionsInteractivelyOpened() {
        // ...
    }
    // 按钮关闭回调
    override func revealOptionsInteractivelyClosed() {
        // ...
    }
    // 点击某个选项,或一次性滑动结束只有一个时与点击一样效果的回调
    override func revealOptionSelected(_ option: ItemListRevealOption, animated: Bool) {
        self.setRevealOptionsOpened(false, animated: true)
        self.revealOptionsInteractivelyClosed()
        // 通过对option的内容,做删除执行等操作
        // ...
    }
}

6、执行删除操作 CallListControllerNode -> init -> callListNodeViewTransition [preparedCallListNodeViewTransition] -> enqueueTransition

CallListControllerNode -> init -> combineLatest(xxx)… mapToQueue{} -> preparedCallListNodeViewTransition -> delete + insert + update -> enqueueTransition -> dequeueTransition -> listNode.transaction

8、手势执行过程逻辑,在父类ItemListRevealOptionsItemNode
8.1、手势向左滑调用过程:
ItemListRevealOptionsItemNode -> didLoad -> view.addGestureRecognizer -> ItemListRevealOptionsGestureRecognizer:UIPanGestureRecognizer -> revealGesture(_:)

8.2、滑动手势的过程revealGesture.changed -> setupAndAddRightRevealNode没有时才添加 -> updateRevealOffsetInternal -> updateRevealOffset(offset:,transition:)

8.3、手势结束过程revealGesture.ended, .cancelled -> updateRevealOffsetInternal -> updateRevealOffset(offset:,transition:)

8.4、setupAndAddRightRevealNode(optionSelected:xx,tapticAction:xx) -> revealOptionSelected -> 执行点击后的操作
8.5、 revealGesture(_:) -> .ended, .cancelled -> isDisplayingExtendedAction -> revealOptions.right.last -> revealOptionSelected -> 执行点击后的操作

This post is licensed under CC BY 4.0 by the author.