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

这也太一言难尽了吧,暂且不说拖动时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; } } }
|
于是得到了这样的效果,好了不少但还是有点丑…特别是这奇怪的阴影。

为了消除这阴影,我们还是首先用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]; }
|
最终效果如下,算是基本上满足了我们的需求吧
