Skip to content

Commit 94cc01a

Browse files
authored
Revise layout and scrolling logic for ordering page
Updated the structure and scrolling behavior of the food ordering page to improve user interaction and navigation.
1 parent 285b623 commit 94cc01a

1 file changed

Lines changed: 147 additions & 74 deletions

File tree

docs/外卖app点餐页面.md

Lines changed: 147 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,150 @@
1-
----------------------------------
2-
| |
3-
| |
4-
| |
5-
| | cover
6-
| |
7-
| |
8-
| |
9-
----------------------------------
10-
| | | | | tab
11-
----------------------------------
12-
| |
13-
| | containerScrollView的header(可能有可能没有)
14-
| | 这个containerScrollView不是指框架内的containerScrollView,是2个子scrollView的父视图
15-
| |
16-
----------------------------------
17-
| | |
18-
| | |
19-
| | |
20-
| | |
21-
| | |
22-
| | |
23-
| | | 左collectionView + 右collectionView
24-
| | |
25-
| | |
26-
| | |
27-
| | |
28-
| | |
29-
| | |
30-
| | |
31-
| | |
32-
| | |
33-
| | |
34-
| | |
35-
| | |
36-
| | |
37-
----------------------------------
38-
39-
40-
类似美团、饿了吗的商品点餐页,在第一个tab下的子VC中有2个子scrollView,左边是分类,右边是列表。并切滑动cover区域时有一个小细节:如果左边或者右边子scrollView的偏移量是非初始状态,也就是contentOffset.y是大于0时,这时上下滑动cover的右边区域(手指触摸点的x值在右边子scrollView的区域内),会先滚动右边子scrollView,滑动cover的左边区域(手指触摸点的x值在左边子scrollView的区域内,会先滚动左边子scrollView
41-
42-
我给出一个实现思路(针对第一个子VC):
43-
最底部是一个容器scrollView(作为本框架中NestedPageScrollable协议里的的nestedPageContentScrollView),其上面添加了2个子scrollView,y值均为0即可,因为本框架会自动设置容器scrollView的contentInset.top和contentOffset.y,2个子scrollVIew会自动显示在tab栏的下方。
44-
45-
3个scrollView大致层级+约束关系如下:
46-
47-
View (Controller.view)
48-
49-
└── containerScrollView (UIScrollView)
50-
│ edges = view.edges
51-
52-
└── contentView (UIView)
53-
│ edges = containerScrollView.contentLayoutGuide.edges
54-
│ width = containerScrollView.frameLayoutGuide.width
55-
56-
├── leftScrollView (UITableView/UICollectionView)
57-
│ top = contentView.top
58-
│ bottom = contentView.bottom
59-
│ leading = contentView.leading
60-
│ width = 30% screen width
61-
62-
└── rightScrollView (UITableView/UICollectionView)
63-
top = contentView.top
64-
bottom = contentView.bottom
65-
leading = leftTableView.trailing
66-
trailing = contentView.trailing
67-
width = 70% screen width
68-
69-
布局完成后,需要保证contentView的高度被撑开,这样containScrollView才会有contentSzie,可以手动计算contentView的高度,也可以重写子scrollView的intrinsicContentSize返回contentSize。
70-
复杂的点在于containerView和2个子scrollView的滚动处理:
71-
1、2个子scrollView禁用滚动,只处理容器scrollView的滚动。这种情况如果右边子scrollView是collectionView并且有多个分组时,sectionHeader需要吸顶,由于collectionView自身不能滚动,可能需要手动计算sectionHeader的吸顶位置
72-
2、containerView和2个子scrollView的滚动可同时发生,但是在合适的时机需要锁定另一个scrollView的滚动...
73-
74-
以上思路只是一个初步思考,需要通过实践去验证,由于时间问题,该示例没有去完成,以后有机会再尝试尝试。
1+
# 外卖 App 点餐页
752

3+
## 核心结构
764

5+
这个页面的关键不是“双列表”,而是“双列表 + 一个明确的主滚动对象”。
776

7+
架构关系如下:
8+
9+
```text
10+
PageViewController
11+
└── 点餐页 VC.view
12+
├── rightListView // 右侧商品列表,主滚动对象
13+
└── leftListContainer // 左侧列表的父视图,不是 rightListView 的子视图
14+
└── leftListView // 左侧分类列表
15+
```
16+
17+
再强调一次:
18+
19+
- 左侧列表不是右侧列表的子视图
20+
- 左侧列表和右侧列表是兄弟视图
21+
- 左侧列表有自己的父视图
22+
- 右侧列表负责承接页面主滚动
23+
24+
之所以左侧要有单独父视图,不只是为了摆放方便,更是为了做裁剪、层级控制和命中区域控制。否则左侧很容易和顶部吸顶区域打架,也容易让人误以为它只是右侧内容上的一个悬浮子 view。
25+
26+
## 为什么右侧是主滚动对象
27+
28+
因为用户真正浏览的是商品,不是分类。
29+
30+
所以职责应该这样分:
31+
32+
- 右侧商品列表负责页面主滚动
33+
- 左侧分类列表负责导航和状态反馈
34+
35+
这比“左右两个列表地位完全对等”更符合真实交互。
36+
37+
## 顶部区域和列表的关系
38+
39+
页面上层还有一套公共结构:
40+
41+
```text
42+
cover
43+
tab
44+
点餐内容区
45+
```
46+
47+
其中点餐内容区内部再拆成左右两列。
48+
49+
更准确地说,应该理解为:
50+
51+
```text
52+
页面整体
53+
├── cover
54+
├── tab
55+
└── 点餐页内容
56+
├── rightListView
57+
└── leftListContainer
58+
└── leftListView
59+
```
60+
61+
也就是说,左侧列表和右侧列表都属于“点餐页内容”这一层,不属于 `cover`,也不属于彼此。
62+
63+
## 滚动的核心思想
64+
65+
这个页面不能理解成“两个列表各滚各的”,也不能理解成“两个列表永远强绑定同一个 offset”。
66+
67+
正确的理解是分两段。
68+
69+
### 1. 顶部还没收起时
70+
71+
这时候页面目标是先把 `cover` 推走,让 `tab` 吸顶。
72+
73+
因此左右列表要表现得像一个整体:
74+
75+
- 左边滚,右边跟
76+
- 右边滚,左边跟
77+
78+
这时同步的是位移。
79+
80+
### 2. 顶部收起后
81+
82+
这时候页面目标从“收起顶部”切换成“浏览商品”。
83+
84+
因此不应该继续强绑位移,而应该改成:
85+
86+
- 右侧继续独立滚商品
87+
- 左侧不再强制同步位移
88+
- 左侧只根据右侧当前位置更新高亮
89+
90+
这时同步的是状态,不是位移。
91+
92+
这一点是整个方案最核心的地方。
93+
94+
## 左侧列表为什么不能简单当成右侧的附属视图
95+
96+
如果把左侧直接理解成右侧上的一个子视图,问题会很多:
97+
98+
- 左侧自己的滚动不好处理
99+
- 顶部区域收起时,两边的联动边界不清楚
100+
- 吸顶后的层级关系容易混乱
101+
- 左侧进入 tab 区域时不容易裁剪
102+
103+
所以更合理的做法是:
104+
105+
- 右侧列表独立作为主滚动区
106+
- 左侧列表独立存在
107+
- 两者通过联动逻辑协作,而不是通过父子关系耦合
108+
109+
## 左右两列真正的关系
110+
111+
左侧和右侧不是“内容并列”关系,而是:
112+
113+
- 左侧是分类导航
114+
- 右侧是商品内容
115+
116+
所以经典交互应该是:
117+
118+
- 左点右滚
119+
- 右滚左亮
120+
121+
最终以右侧当前位置作为真实内容位置,左侧只是表达这个位置。
122+
123+
## 分组标题的关键点
124+
125+
右侧商品列表通常会有分组标题,但这里不能机械地套系统默认吸顶。
126+
127+
因为页面顶部已经有 `cover + tab` 的吸顶体系,如果分组标题再直接走默认吸顶,很容易出现:
128+
129+
- 吸顶位置不对
130+
- 和 tab 冲突
131+
- 多层吸顶过渡不自然
132+
133+
所以分组标题的处理原则应该是:
134+
135+
- 服从整页吸顶结构
136+
- 必要时自己接管展示逻辑
137+
138+
重点不是“必须用系统 sectionHeader”,重点是最终吸顶效果是否正确。
139+
140+
## 一句话总结
141+
142+
这个页面的本质是:
143+
144+
```text
145+
一个主商品列表
146+
+ 一个独立分类列表
147+
+ 一个左/右分阶段联动机制
148+
```
149+
150+
只要这三个点立住,具体实现细节都可以继续调整。

0 commit comments

Comments
 (0)