ios scrollview嵌套tableview同向滑动的示例

时间:2022-08-30 20:48:30

我讨论的问题是嵌套同向滑动,能避免尽量避免。最好用一个tableview实现。一个tableview不够用了再嵌套,适用复杂场景。

首先我说下不适用的,免得大家浪费时间。

1.不适用上下拉刷新加载更多的页面。

2.不适用点击cell获取点击事件的页面,可以加入button点击获取事件。

官方文档说尽量不要进行两个竖直或两个水平方向滑动的视图嵌套。因为这个时候机器不知道用户要让哪个滑动,但在我们这个神奇的国度,项目中经常出现这样的需求,产品经理总爱这样做,andriod那边是比较容易实现的,ios这边十分复杂,我研究了一天,写了个demo,可以勉强实现,我的项目中就有上下拉,因此我就硬嵌套了,用户滑动的时候不能准确地按自己的意愿滑动scrollview、tableview。就这样了,这个没有解决方案的。

我做到的效果是手点在哪个视图上,哪个视图就滚动,当小的scroll滚到到自己的临界值就滚动大的scroll,当大的也到临界值就不滚动。顺便实现了一个伪悬浮的secview如果没有那个悬浮的就把那个悬浮高度floatviewheight置0。剩下的根据页面调整frame即可通用。

这是效果图

ios scrollview嵌套tableview同向滑动的示例

下面我说一下在没有以上两点不适用的页面的实现的思路:

scrollview在控制器的view上,是一个大的视图,tablewview在scrollview上。根据拖拽手势配合,先判断首先触摸点在哪个view上,再对哪个view滑动操作。解决手势和button点击冲突。下面看代码,注释十分清晰。github有demo,欢迎阅读: https://github.com/qingyindaoren/scrollinsettable.git

核心代码如下:

?
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
#import "viewcontroller.h"
#import "yygesturerecognizer.h"
#import "scrolltableviewcell.h"
#import "mbprogresshud+add.h"
#define screenwidth [uiscreen mainscreen].bounds.size.width
#define screenheight [uiscreen mainscreen].bounds.size.height
//虚假的悬浮效果
static cgfloat floatviewheight = 30.0;
 
static cgfloat navheitht = 64;
// 这个系数根据自己喜好设置大小,=屏幕视图滑动距离/手指滑动距离
#define movescale 2
 
 
@interface viewcontroller ()<uitableviewdelegate,uitableviewdatasource,uigesturerecognizerdelegate>
@property (nonatomic,weak)uiscrollview *scroll;
@property (nonatomic, strong) nsarray *titles;
@property (nonatomic,weak)uitableview *insettableview;
@property (nonatomic,assign)cgfloat tabley;
@property (nonatomic,assign)cgfloat tablestarty;
@property (nonatomic,assign)cgfloat scrolly;
@property (nonatomic,assign)cgfloat scrollstarty;
 
//tableview 的y值 在scrollview中的位置
@property (nonatomic,assign)cgfloat tableframey;
@end
 
@implementation viewcontroller
 
- (void)viewdidload {
 
  [super viewdidload];
  // do any additional setup after loading the view, typically from a nib.
  self.view.backgroundcolor = [uicolor whitecolor];
  self.title = @"scrollscroll";
// 有导航最上部视图是scrollview 内部空间位置会下移,设置这个属性后不下移。
  if ([self respondstoselector:@selector(setautomaticallyadjustsscrollviewinsets:)]) {
    self.automaticallyadjustsscrollviewinsets = no;
  }
 
  uiscrollview *scroll = [[uiscrollview alloc]initwithframe:cgrectmake(0,navheitht, screenwidth, screenheight-navheitht)];
  scroll.backgroundcolor = [uicolor colorwithred:0.4 green:0.3 blue:0.2 alpha:1.0];;
  
 
  [self.view addsubview:scroll];
  self.scroll = scroll;
  
  
  //根据需求设置tableview的y值 暂写scroll高的2分之一
   self.tableframey = self.scroll.frame.size.height/2;
  
  uiimageview *headimage = [[uiimageview alloc]initwithframe:cgrectmake(0, 0, screenwidth, self.tableframey-floatviewheight)];
  headimage.image = [uiimage imagenamed:@"scrollhead"];
  headimage.contentmode = uiviewcontentmodescaleaspectfill;
  [self.scroll addsubview:headimage];
  
  nsarray *titles = @[@"ico详情",@"央行放大招",@"比特币会涨",@"神秘中本村"];
  self.titles = titles;
   uisegmentedcontrol *segment = [[uisegmentedcontrol alloc] initwithframe:cgrectmake(5, scroll.bounds.size.height/2-30, self.scroll.bounds.size.width - 10, 30)];
   [segment addtarget:self action:@selector(segmentvaluechanged:) forcontrolevents:uicontroleventvaluechanged];
  for (nsstring *title in _titles) {
    [segment insertsegmentwithtitle:title atindex:segment.numberofsegments animated:false];
  }
  segment.selectedsegmentindex = 0;
  [self.scroll addsubview:segment];
  
  uitableview *insettable = [[uitableview alloc]initwithframe:cgrectmake(0,self.tableframey, self.view.bounds.size.width, screenheight-navheitht-floatviewheight)];
  insettable.backgroundcolor = [uicolor colorwithred:0.9 green:0.9 blue:0.9 alpha:1.0];
  
  insettable.datasource = self;
  insettable.delegate = self;
  
 
  [self.scroll addsubview:insettable];
  self.insettableview = insettable;
  
//github搜索 yykit 或yytext 里面有 yygesturerecognizer这个类,这个类需要做一些修改,  // 在yygesture中所有触摸事件方法里 加上super的方法,原文件里没有,否则响应链条中断,scroll或tablew的按钮点击事件不执行。
  //这个类原文件继承于uigesturerecognizer, 改为继承于uipangesturerecognizer 否则点击事件不执行。
  //运行效果详见我的demo
 
  yygesturerecognizer *yyges = [yygesturerecognizer new];
  yyges.action = ^(yygesturerecognizer *gesture, yygesturerecognizerstate state){
    if (state != yygesturerecognizerstatemoved) return ;
    
    if (cgrectcontainspoint(self.insettableview.frame, gesture.startpoint)) {
     
     //滑动tableview
      [self tablescrollwithgesture:gesture];
      
      
 
    }else{
      
      //滑动scrollview
      [self scrollscrollwithgesture:gesture];
      
    }
 
  };
  //必须给scroll 加上手势 不要给view加,不然滑动tablew的时候会错误判断去滑动scroll。
  [self.scroll addgesturerecognizer:yyges];
  
  //实现手势代理,解决交互冲突
  yyges.delegate = self;
   scroll.contentsize = cgsizemake(self.view.bounds.size.width, self.tableframey+self.insettableview.frame.size.height);
 
}
//解决手势按钮冲突
- (bool)gesturerecognizer:(uigesturerecognizer *)gesturerecognizer shouldreceivetouch:(uitouch *)touch
{
  //如果是 segment或scroll上的其他按钮,取消手势
  if([nsstringfromclass(touch.view.superclass) isequaltostring:@"uicontrol"]){
    return no;
  }
 
 
  //
    return yes;
    }
//
- (void)segmentvaluechanged:(uisegmentedcontrol *)segment {
//scroll 到底部
  cgfloat offset = self.scroll.contentsize.height - self.insettableview.bounds.size.height-floatviewheight;
  if (offset > 0)
  {
    self.scrolly = offset;
    [self.scroll setcontentoffset:cgpointmake(0, offset) animated:yes];
  }
  //tableview到顶部
  self.tabley = 0;
  [self.insettableview setcontentoffset:cgpointmake(0, self.tabley) animated:yes];
}
- (void)tablescrollwithgesture:(yygesturerecognizer *)gesture{
  cgfloat scrolly;
  
  if (self.tablestarty != gesture.startpoint.y) {
    scrolly = -(gesture.currentpoint.y-gesture.startpoint.y) ;
  }else{
    scrolly = -(gesture.currentpoint.y-gesture.lastpoint.y) ;
  }
  self.tablestarty = gesture.startpoint.y;
  
  self.tabley += scrolly*movescale;
  
  //为了显示底部超出屏幕的tableview那部分 滑动scrollview 此时tablewview已经滑动到了底部
  if (self.tabley> self.insettableview.contentsize.height-self.insettableview.bounds.size.height){
    self.scrolly += self.tabley-(self.insettableview.contentsize.height-self.insettableview.bounds.size.height);
    
    //tablewview滑动到底部就不要滑了
    self.tabley = self.insettableview.contentsize.height-self.insettableview.bounds.size.height;
    
  //scrollview 滑动到了底部就不要滑动了
    if (self.scrolly> self.scroll.contentsize.height-self.insettableview.bounds.size.height-floatviewheight){
      self.scrolly = self.scroll.contentsize.height-self.insettableview.bounds.size.height-floatviewheight;
      //如果scrollview意外的contentsize 小于自己的大小,scrollview就不要滑了
      if (self.scrolly<0) {
        self.scrolly = 0;
      }
      
    }
    [self.scroll setcontentoffset:cgpointmake(0, self.scrolly) animated:yes];
    
    //如果tablewview的cell过少或行高过少致使其contentsize 小于自己的大小,tableview就不要滑了
    if (self.tabley<0) {
      self.tabley = 0;
    }
    
  }
  
  
  //如果滑到了tableview的最上部,停止滑动tablewview, 如果此时scrollview 没有在最上部就滑动scrollview到最上部
  if (self.tabley<0){
    self.scrolly += self.tabley;
    
    //scroll已经在最上部了,scroll就不滑了
    if (self.scrolly<0) {
      self.scrolly = 0;
    }
    
    nslog(@"scroll %lf",self.scrolly);
    [self.scroll setcontentoffset:cgpointmake(0, self.scrolly) animated:yes];
    
     //停止滑动tablewview
    self.tabley = 0;
    
  }
  nslog(@"table %lf",self.tabley);
  
  
  [self.insettableview setcontentoffset:cgpointmake(0, self.tabley) animated:yes];
}
- (void)scrollscrollwithgesture:(yygesturerecognizer *)gesture{
  cgfloat scrolly;
  
  if (self.scrollstarty != gesture.startpoint.y) {
    scrolly = -(gesture.currentpoint.y-gesture.startpoint.y) ;
  }else{
    scrolly = -(gesture.currentpoint.y-gesture.lastpoint.y) ;
  }
  self.scrollstarty = gesture.startpoint.y;
  
  self.scrolly += scrolly*movescale;
  
  //如果滑到了scroll的底部就不要滑了
  if (self.scrolly> self.scroll.contentsize.height-self.insettableview.bounds.size.height-floatviewheight){
    self.scrolly = self.scroll.contentsize.height-self.insettableview.bounds.size.height-floatviewheight;
    //如果scrollview意外的contentsize 小于自己的大小,scrollview就不要滑了
    if (self.scrolly<0) {
      self.scrolly = 0;
    }
  }
  //如果滑到了scroll顶部就不要滑了
  if (self.scrolly<0){
    self.scrolly = 0;
  }
  nslog(@"scroll %lf",self.scrolly);
  
  
  [self.scroll setcontentoffset:cgpointmake(0, self.scrolly) animated:yes];
  
}
 
 
#pragma mark - 展示tableview的代理
 
- (cgfloat)tableview:(uitableview *)tableview heightforrowatindexpath:(nsindexpath *)indexpath {
  return 70;
}
 
- (nsinteger)tableview:(uitableview *)tableview numberofrowsinsection:(nsinteger)section {
  return 10;
}
 
 
- (uitableviewcell *)tableview:(uitableview *)tableview cellforrowatindexpath:(nsindexpath *)indexpath {
  
  scrolltableviewcell * cell = [tableview dequeuereusablecellwithidentifier:@"scrolltableviewcell"];
  if (!cell)
  {
    [tableview registernib:[uinib nibwithnibname:@"scrolltableviewcell" bundle:nil] forcellreuseidentifier:@"scrolltableviewcell"];
    cell = [tableview dequeuereusablecellwithidentifier:@"scrolltableviewcell"];
  }
  
    cell.backgroundcolor = [uicolor clearcolor];
   
 
  
  cell.selectionstyle = uitableviewcellselectionstylenone;
  cell.titletext.text = [nsstring stringwithformat:@"\t第%zd行",indexpath.row];
  cell.detailtext.text = @"滑屏呀滑屏呀划呀";
  cell.detailtext.textcolor = self.navigationcontroller.navigationbar.tintcolor;
  cell.indexpath = indexpath;
  
  cell.selectcellblock = ^(nsindexpath *indexpath) {
    nsstring *tip = [nsstring stringwithformat:@"点击了第%ld组%ld行",indexpath.section,indexpath.row];;
    [mbprogresshud showmessage:tip view:nil];
    
    nslog(@"%@",tip);
 
  };
  
  return cell;
}
- (nsinteger)numberofsectionsintableview:(uitableview *)tableview{
  
  return 3;
}
-(uiview *)tableview:(uitableview *)tableview viewforheaderinsection:(nsinteger)section{
  uiview *v = [[uiview alloc]initwithframe:cgrectmake(0, 0, screenwidth, 50)];
  v.backgroundcolor = [uicolor orangecolor];
  uilabel *l = [[uilabel alloc]initwithframe:v.bounds];
  l.text =[nsstring stringwithformat:@"tableview的组头%ld",section];
  l.textcolor = [uicolor whitecolor];
  l.textalignment = nstextalignmentcenter;
  [v addsubview:l];
  return v;
}
//组头高
-(cgfloat)tableview:(uitableview *)tableview heightforheaderinsection:(nsinteger)section{
  
  return 50;
  
  
}
//这个方法不可用了,除非点击了cellcontenview之外的区域 只能同过加按钮的方式接受点击事件
-(void)tableview:(uitableview *)tableview didselectrowatindexpath:(nsindexpath *)indexpath{
  nslog(@"点击了第%ld行",indexpath.row);
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:http://www.jianshu.com/p/ff26982daf61