关于这个标题,起因是这样的。
最近一次做项目需求时,遇到这样一个需求,就是本来我们App是必须注册或者第三方登录才可以使用,现在希望不登录也可以浏览App里面的内容,只是在需要的时候才提示登录,并且在用户没有登录的情况下,用户选择并登录成功了,程序需自动完成用户操作登录前的操作。比如购买商品时没有登录,用户登录成功后,直接跳转至订单确认页面。
在接到这个需求时,我们的App功能已经很多了,评估了下这个需求,发现App里面很多功能是需要登录才可以操作,比如关注用户、购买商品、私信聊天、评论等等,而且这些功能的入口也比较多。
这么多的地方我们都要去写判断的代码显然是不科学的,那么有没有简单点的方式呢?怎么避免我们去做苦力活呢?????????????
于是,进一步分析,发现这些功能大部分都是用户主动通过点击按钮来触发下一步操作。此时,我们把关注点移到按钮UIButton上。
最开始想到的办法是自定义一个button,让所有需要登录操作的按钮继承这个按钮,然后,在这个按钮里面拦截自身事件进一步处理。但是,发现这么做还是需要改大量的代码。接着想到用类别来做,这样直接给按钮增加一个BOOL属性,设置为YES的按钮视为需要做登录才可以操作的按钮。然后,对于需要登录操作的按钮,在分类里面拦截其点击事件,并记录target和action,然后先判断是否登录:如果没有登录则丢弃其target和action,并且提示用户登录;如果用户已经登录或者登录成功了,则继续让target执行action,这样完美解决我们的需求,也只需要很少的代码即可搞定。
这个方案看似很不错,不过在实际做的时候还是走了弯路。一开始,我们想从下面方法入手
- (void)addTarget:(nullableid)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents
但是发现根本就不能实现。经过查找,找到了下面这个方法:
- (void)sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event
关于这个方法,苹果给了如下解释:
send the action. the first method is called for the event and is a point at which you can observe or override behavior. it is called repeately by the second.
这正是我们要找的方法,于是我们重写此方法,如下:
- (void)sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event
{if(self.checkLogin)
{ self.selector = NSStringFromSelector(action); self.objClass = target;
[self checkIsLogin];
}else
{
[super sendAction:action to:target forEvent:event];
}
}
- (void)checkIsLogin
{
__weak typeof(self) weakSelf = self;
[LoginManager checkLoginSuccess:^{
SEL sel = NSSelectorFromString(weakSelf.selector);if([weakSelf.objClass respondsToSelector:sel])
{if([weakSelf.selector hasSuffix:@":"])
{
objc_msgSend(weakSelf.objClass, sel, self);
}else
{
objc_msgSend(weakSelf.objClass, sel);
}
}
}];
}
简单解释下这段代码:
当按钮事件执行时会走sendAction:to:forEvent:这个方法,于是,我们在这个方法里面,先判断该按钮是否需要登录后再操作,如果需要,阻断事件传递,并记录下按钮的action和target,然后判断是否登录了,如果已经登录或者用户登录成功了,那么再调用objc_msgSend(self.objClass, self.selector)去实现按钮事件,如果用户放弃登录或者登录失败,则不做处理。
实现了上面的方法之后,我们只需要找出那些按钮事件需要登录后才能操作,然后,设置按钮的checkLogin = YES即可,这样是不是省了很多不必要的代码。
到此,上面的实现已经解决了所有按钮点击需要判断登录的操作。还有些是上述方式解决不了的,则使用LoginManager单独处理下,幸运的是,几乎很少地方需要单独处理。
通过这个案例:一方面巩固了对sendAction:to:forEvent:这个方法的理解;另一方面在做需求的时候一定要发散思维,找到更合理的解决方法。
欢迎大家留言讨论,如果你有更好地方法,欢迎分享!
网友评论