Contents

 今晚准备给一个tableView写个拖拽排序功能,心想UITableView有这个功能,回去写两个代理方法就可以咸鱼了。
 结果emmmmm…

Image
 这也太一言难尽了吧,暂且不说拖动时cell在鬼畜,这丑陋的按钮和拖动条也不能忍啊。略经思考我大概懂得了鬼畜的原因:我为了设置cell之间的间隔像下面这样重写了cell的setFrame:方法,而拖动时系统会持续修改cell的frame,于是…

1
2
3
4
5
- (void)setFrame:(CGRect)frame{
frame.origin.y += 14;
frame.size.height -= 14;
[super setFrame:frame];
}

 总算是没那么鬼畜了,可是这大红色的按钮还是很碍眼,我只想要我简简单单的cell,于是

 好吧我们看看文档怎么说

 好吧设置了tableView:editingStyleForRowAtIndexPath:方法,那个红艳艳的按钮是不见了,可是还有一段奇怪的indent,我注意到UITableViewCell有个shouldIndentWhileEditing属性,当我尝试设置这个属性为NO多次未果后我意识到是不是要在tableView:editingStyleForRowAtIndexPath:方法返回之后设置shouldIndentWhileEditing才有效,于是我把cell.shouldIndentWhileEditing=NO放在了tableView willDisplayCell:forRowAtIndexPath:方法里,indent消失了,好坑啊…

 接下来我们还要除去这碍眼的拖动条,我在UITableView和UITableViewCell的文档里仅仅发现了一个showsReorderControl属性,然而,在设置了该属性为NO后,我就完全无法拖动cell了!而我想要的是可以触摸任何位置拖动的cell啊。而这个拖动条控件似乎是个私有属性,那么我们似乎不得不用点黑魔法了。以下代码通过runtime获取cell的属性列表并打印出来。

1
2
3
4
5
6
7
8
unsigned int count=0;
Ivar * properties=class_copyIvarList([UITableViewCell class], &count);
for (int i=0; i<count; i++) {
Ivar var=properties[i];
const char * varName=ivar_getName(var);
const char * varType=ivar_getTypeEncoding(var);
NSLog(@"%s--%s",varName,varType);
}

 然而,很尴尬,打出来的log中并没有可疑的属性。苦思未果的我只好用了下面这种更dirty的办法,通过遍历cell的subviews找到拖动条,将其图片设置为nil并将其拉伸为整个cell的大小。要是哪位大神知道比较优雅的方法还望指教…【拱手】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- (void)hideReorderControlForCell:(CategoryManagerCell *)cell{
if (!cell.hasHideReorderControl) {
for (UIView * view in cell.subviews) {
Class reorderClass = NSClassFromString(@"UITableViewCellReorderControl");
if ([view isMemberOfClass:reorderClass]) {
[view mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(cell.mas_left);
make.right.equalTo(cell.mas_right);
make.top.equalTo(cell.mas_top);
make.bottom.equalTo(cell.mas_bottom);
}];
for (UIImageView * imgView in view.subviews) {
imgView.image=nil;
}
}
}
cell.hasHideReorderControl=YES;
}
}

后记:我突然想到了个稍微优雅点但可能还是很不规范的实现,我们可以重写cell的addSubview:,如果加入的view是UITableViewCellReorderControl类的,就做一些处理,像下面这样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (void)addSubview:(UIView *)view{
[super addSubview:view];
if ([view isMemberOfClass:NSClassFromString(@"UITableViewCellReorderControl")]) {
[view mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.mas_left);
make.right.equalTo(self.mas_right);
make.top.equalTo(self.mas_top);
make.bottom.equalTo(self.mas_bottom);
}];
for (UIImageView * imgView in view.subviews) {
imgView.image=nil;
}
}
}

 于是得到了这样的效果,好了不少但还是有点丑…特别是这奇怪的阴影。
Image 2
 为了消除这阴影,我们还是首先用runtime方法查看UITableView的私有属性,然而我才疏学浅,找遍了UITableView和UITableViewCell的私有方法和实例变量都没能找到相关方法,只是知道了这阴影是由两个UIShadowView组成的,看来得用点更黑的魔法了…
 我们首先通过class_copyMethodList()获得UIShadowView的方法列表,在打出的log中可以看到setShadowImage:forEdge:inside:这个方法很可疑,接下来我们就着手用我们的方法替代setShadowImage:forEdge:inside:方法,具体代码如下.

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
- (void)exchangeShadowMethod{
Class class = objc_getClass("UIShadowView");
// unsigned int count=0;
// Method * methods = class_copyMethodList(class, &count);
// for (int i=0; i<count; i++) {
// Method method=methods[i];
// const char * methodName=sel_getName(method_getName(method));
// const char * methodType=method_getTypeEncoding(method);
// NSLog(@"%s--%s",methodName,methodType);
// }
SEL originalSelector = @selector(setShadowImage:forEdge:inside:);
SEL mySelector = @selector(fakeMethodForShadow);
if (class_respondsToSelector(class, originalSelector)) {
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method myMethod = class_getInstanceMethod([self class], mySelector);
IMP originalImp = method_getImplementation(originalMethod);
IMP myImp = method_getImplementation(myMethod);
class_replaceMethod([self class], mySelector, originalImp, method_getTypeEncoding(myMethod));
class_replaceMethod(class, originalSelector,myImp, method_getTypeEncoding(originalMethod));
}
}

- (void)fakeMethodForShadow
{

}

 我们所做的就是交换了setShadowImage:forEdge:inside:和我们的fakeMethodForShadow的实现,接下来我们为了我们的黑魔法不影响应用的其他部分,在viewDidAppear和ViewDidDisappear中做些调整即可。

1
2
3
4
5
6
7
8
9
- (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
[self exchangeShadowMethod];
}

- (void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
[self exchangeShadowMethod];
}

 最终效果如下,算是基本上满足了我们的需求吧
Image 3

Contents