登录功能是我在湖畔做的第一个需求。
当时PD给我的草图和下图类似:
(图片来自知乎iOS客户端登录界面)
不过需求中要求用户名或者密码错误时,输入框要抖动(类似Mac登录密码错误的抖动效果)。
如果实现上图的UI布局,那么输入框抖动是上下单元格独立抖动还是整体抖动?
- 独立抖动:会出现上下单元格边界不齐的断裂效果,破坏美感。
- 整体抖动:只是用户名错误,密码框为什么抖动?给用户的提示不清晰。
我个人不希望给用户不友好的信息,所以我做成了下面的效果:
由于我做的效果和PD想要的不一致,所以还产生了一番讨论。
为此,不得不向PD普及一下网站登录检查的基本流程,让他知道会先检查用户名是否存在,才会进一步判断用户名和密码是否匹配。
我是以前写过一段时间PHP做网站,自然而然就这么想了。那么,PD同学不了解相关知识,是否可以理解呢?
突然有疑惑,一毕业就在互联网行业当产品经理的同学,大多是什么专业毕业,应该具备哪些专业背景呢?我个人认为核心素质之一是站在用户的角度思考问题。这里是知乎的一份讨论。
最后,PD同学在被服务器端同学鄙视了一下的情况下,也赞同了该方案。
在这个模块中,主要涉及到UITextField以及一些用户输入交互特性。
以用户名输入框为例,上面两张登录图都可以输入Email,那么此时的键盘应该呈什么布局呢?
不妨看下几个客户端的登录界面:
(湖畔iOS版登录界面)
刚开始做登录模块时,我将键盘设置为UIKeyboardTypeEmailAddress类型,不过后来PD一直强调要改回英文键盘(即UIKeyboardTypeAlphabet类型)。因为对于中文用户来说,EmailAddress类型键盘默认是中文的,输入过程中会有中文提示产生,如下面第二张知乎截图。
不过我们来认真探究下用户的输入体验,以jasonlee.ljp@gmail.com为例:
- UIKeyboardTypeAlphabet:用户在输入点号或者@符号时需要进行键盘切换,所以在输入上述邮箱时一共要切换6次键盘类型。
- UIKeyboardTypeEmailAddress:看起来在输入过程中会产生中文字符,不过该类型的键盘布局包含了 @ 符号和 . 圆点符号。当输入@符号或者点号时,用户输入的所有字符会作为英文字母放进输入框中,只需要在最后敲一下“确认”键。
所以,就我个人的体验来说,我认为是UIKeyboardTypeEmailAddress类型是更友好的。
(知乎iOS版登录界面)
当我为湖畔用户名输入框设置为UIKeyboardTypeEmailAddress类型时,我特地看了下知乎登录界面的用户名输入框,那时候知乎采用的是UIKeyboardTypeAlphabet类型。当时我沾沾自喜了下,没想到现在颠倒过来。谁在进步,谁在退步?
(新浪微博iOS版登录界面)
新浪微博的键盘默认也是UIKeyboardTypeEmailAddress类型的,更进一步说明了问题。
其实,苹果本身特地提供了UIKeyboardTypeEmailAddress类型键盘就很说明问题了。
(微信iOS版)
微信一直广受好评,尤其是最近关于“张小龙”、“微信”、“产品”的热门词汇(1,2,3),更是将其拥上了另一个高台。
不过,就我浅薄的认知,我认为上面两张微信截图的键盘布局都应该是UIKeyboardTypeAlphabet类型的。
首先看“修改微信号”界面。微信号只能包含字母、数字、下划线和减号,那么设置UIKeyboardTypeEmailAddress键盘类型并没有为用户提供便利,且由于上面提过的默认中文键盘问题,用户在输入过程中会有中文字符产生,相较于流畅的纯英文输入,会有“阻碍感”和“不爽感”。我个人觉得这是正常的用户心理,当然,也有可能我不是正常的用户?
另外,登录界面就多了QQ号和手机号两种账号类型,且都是纯数字的,所以这里的帐号约等于微信号。那么,这里的键盘布局为什么和“修改微信号”的键盘布局不一致,使用默认的中文键盘呢?
所以,我个人认为,在这两个界面中,中文键盘对用户来说都是一种阻碍,应该设置为UIKeyboardTypeAlphabet类型。
腾讯是很追求产品细节和用户体验的,微信更是腾讯产品中的佼佼者,为何会存在这样的细节问题?或者是由于我视野太局限,看不懂?
(QQ登录界面)
QQ的新界面还是令人耳目一新,颇有档次的。而键盘布局也没什么特别好说的,就是纯数字。
--------------------------------------------------
除了键盘类型的使用:_usernameText.keyboardType = UIKeyboardTypeAlphabet;,UITextField还涉及了其它一些细节处理:
- 设置边框类型:[_usernameText setBorderStyle:UITextBorderStyleRoundedRect];。关于边框类型,这里有详细说明。
- 设置默认文案:_usernameText.placeholder = TEXT_LOGIN_NAME_PLACEHOLDER;,给用户友好提示。
- 设置控件内容的对齐方式:_usernameText.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;,这里有详细说明。
- 设置首字符是否默认大写:_usernameText.autocapitalizationType = UITextAutocapitalizationTypeNone;。
- 设置是否开启纠错提醒:_usernameText.autocorrectionType = UITextAutocorrectionTypeNo;。
- 设置何时提供clear按钮:_usernameText.clearButtonMode = UITextFieldViewModeWhileEditing;,这里有相关说明。
- 设置成为焦点:[_usernameText becomeFirstResponder];。当界面中除了输入框和登录按钮外,最好一开始就让输入框成为响应者,好让键盘遮掉空白部分。
- 设置是否密文显示:_userpwdText.secureTextEntry = YES;。如果是密码输入框,当然要了。
- 设置回车键类型:_usernameText.returnKeyType = UIReturnKeyNext;,这里有更多说明。通常,输入完用户名,我们还需要输入密码,所以设置为UIReturnKeyNext类型;而输入完密码,就完成输入要进行登录了,所以要设置为UIReturnKeyDone类型。
最后,我们还要设置代理:_usernameText.delegate = self;。
通过代理方法,我们可以进行更多的控制,比如:
- - (BOOL)textFieldShouldReturn:(UITextField *)textField:响应回车键处理。
- - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string:做一些过滤处理。比如SO上有一份关于退格键检测的讨论。
更多可参见官方文档。
--------------------------------------------------
不过在国内这样隐私堪忧的环境下,需要手机号来注册会流失不少用户。即便是新浪微博这样的应用,需要绑定手机号也令我不信任。除非是像淘宝、支付宝这样需要手机号来提高安全等级的服务,才能弱化用户的心理障碍。
首先,看下手机号码注册。
(注册湖畔)
对于手机号码输入框,我们当然要默认使用UIKeyboardTypeNumberPad类型键盘。不过对于越狱用户,如果装了其它输入法,则有可能使用其它类型键盘输入非数字字符。对此,我们在客户端最好进行过滤和检查。因为客户端如果发送了包含非数字字符的电话号码给服务端进行校验,是没有意义且浪费流量的。
在用户的输入过程中,我们可以通过代理方法来判断输入是否合法,从而进一步决定是否接受输入。
下面是示例代码片段:
- - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
- {
- return [ValidationHelper validateNumeric:string];
- }
- ...
- + (BOOL)validateNumeric:(NSString *)str2validate
- {
- NSCharacterSet *charSet = [[NSCharacterSet characterSetWithCharactersInString:@"0123456789"] invertedSet];
- NSRange range = [str2validate rangeOfCharacterFromSet:charSet];
- return (range.location == NSNotFound) ? YES : NO;
- }
上面只是部分代码示例,比如还缺少判断是否为退格键、对输入框长度进行限制。
同时,为了对用户更友好,我们可以对电话号码进行格式化。比如采用类似+86 137-5555-6666的格式,或者省略+86。这样可以让用户在输入过程中清楚地知道自己输入了多少位数字、输入了哪些数字,不至于担心输错号码或者多一位、少一位。从这个角度看,对输入框长度进行一定限制,也是对用户稍微友好的。
对于获取验证码,这里是由客户端来做频率限制。此处有一个细节:当点击“获取验证码”按钮后,焦点应该放在验证码输入框,因为用户的下一步动作就是输入验证码。我个人比较注意这样的细节,比如浏览器新开一个tab,焦点是否放在地址栏;比如我在群里点击搜索按钮要搜索群用户,搜索框是否处于激活状态供用户输入。这些都是为用户,或者说作为用户,考虑的细节体验。我给阿里旺旺和淘宝浏览器提过这方面的建议。
除了在用户输入过程中对非法字符进行过滤,提交给服务端时也要再检查一次。
用户的任何输入都是不可信的数据,同样地,服务端对于客户端提交的任何数据也要再检查一次,不管客户端是否已经检查过。
比如在提交时,至少要检查下验证码是否为空。如果为空,要不要弹窗呢?我觉得没有必要用UIAlertView来给用户提示,有点重!在这里,我采用的是抖动提示,用户一看就明了。
- //TextField的晃动:Begin
- @interface UITextField(shake)
- - (void)shake;
- @end
- @implementation UITextField(shake)
- - (void)shake
- {
- CAKeyframeAnimation *animationKey = [CAKeyframeAnimationanimationWithKeyPath:@"position"];
- [animationKey setDuration:0.5f];
- NSArray *array = [[NSArrayalloc] initWithObjects:
- [NSValuevalueWithCGPoint:CGPointMake(self.center.x, self.center.y)],
- [NSValuevalueWithCGPoint:CGPointMake(self.center.x-5, self.center.y)],
- [NSValuevalueWithCGPoint:CGPointMake(self.center.x+5, self.center.y)],
- [NSValuevalueWithCGPoint:CGPointMake(self.center.x, self.center.y)],
- [NSValuevalueWithCGPoint:CGPointMake(self.center.x-5, self.center.y)],
- [NSValuevalueWithCGPoint:CGPointMake(self.center.x+5, self.center.y)],
- [NSValuevalueWithCGPoint:CGPointMake(self.center.x, self.center.y)],
- [NSValuevalueWithCGPoint:CGPointMake(self.center.x-5, self.center.y)],
- [NSValuevalueWithCGPoint:CGPointMake(self.center.x+5, self.center.y)],
- [NSValuevalueWithCGPoint:CGPointMake(self.center.x, self.center.y)],
- nil];
- [animationKey setValues:array];
- [array release];
- NSArray *times = [[NSArrayalloc] initWithObjects:
- [NSNumbernumberWithFloat:0.1f],
- [NSNumbernumberWithFloat:0.2f],
- [NSNumbernumberWithFloat:0.3f],
- [NSNumbernumberWithFloat:0.4f],
- [NSNumbernumberWithFloat:0.5f],
- [NSNumbernumberWithFloat:0.6f],
- [NSNumbernumberWithFloat:0.7f],
- [NSNumbernumberWithFloat:0.8f],
- [NSNumbernumberWithFloat:0.9f],
- [NSNumbernumberWithFloat:1.0f],
- nil];
- [animationKey setKeyTimes:times];
- [times release];
- [self.layeraddAnimation:animationKey forKey:@"TextFieldShake"];
- }
- @end
- //TextField的晃动:End
(注册微信)
上面讨论到通过客户端来对验证码请求频率做限制,除此之外,还可以通过服务端来做限制。
微信应该就是通过服务端来做限制的,因为我杀掉进程等了好久,然后重新尝试注册,还一直提示我“发送请求太快,稍后再试”,尝试了好几次都是这样的提示,感觉体验不好。我Google了一下这个提示,发现有不少用户有相同困惑。
Server端永远不要信任Client端(包括Browser)的数据。但如微信这样的产品,在注册获取验证码环节上,对交互的把握,仍然会让用户觉得不爽。
所以,在验证码获取上,我个人倾向客户端控制或者C/S结合。因为至少客户端控制可以让用户有个心理预期,比如60秒的时间。而像微信这样,我根本不知道“休息一下”是休息多久!我尝试了1分钟、5分钟甚至10分钟后再次操作过,还是遇到这个提示。
除此之外,还有一些我个人认为的不足之处:
- 手机号码没有做长度限制。或许各个国家号码长度不一,但如上图,未免也太长了吧?而且微信的大多数用户都来自大陆,完全可以为“+86”的用户做下体验优化。我认为服务了80%用户的功能是十分有价值的。
- 验证码长度没有限制。如果说电话号码长度不可控,那么验证码的长度应该是可控的吧?这个“休息一下,稍后再试”的提示在获取验证码时会有,再校验验证码时也会有!有点烦。如果说控制获取验证码频率可以节约短信费用,那么校验验证码的频率控制又是为何?
- 验证码连基本判空都没有。我没有输入验证码,直接点击下一步,得到如上“验证码不正确”的弹窗提示。如果说发生了服务端验证,那不应该把空串交给服务端验证;如果说客户端验证通不过,那提示有点冗余,而且不精准。
(短信注册新浪微博)
如果说控制验证码发送频率是节能环保省钱的考虑,那么新浪微博直接把这个问题交给用户了。
说实话,当我看到这个webview加载出来的内容时,一股有关部门气息迎面扑来。什么时候,在(大陆)互联网应用上注册帐号还要花钱了?虽然只是一毛钱短信费用。但就这一毛钱,也可以感受到新浪微博并没有足够为用户考虑。(当然,这里要强调下,应该支持正版,要有消费服务的意识。插曲。)
尤其是,这个接收号码还这么长,足足12位!
就不多吐槽了。
--------------------------------------------------
再看看邮箱帐号注册。
(注册湖畔)
邮箱注册过程中可关注的两个基本点是:
- 键盘应设置为UIKeyboardTypeEmailAddress类型。
- 对邮箱格式进行检查。
下面是一段检查邮箱的示例代码:
- + (BOOL)validateEmail:(NSString *)str2validate
- {
- NSString *emailRegex = @"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}";
- NSPredicate *emailPredicate = [NSPredicatepredicateWithFormat:@"SELF MATCHES %@", emailRegex];
- return [emailPredicate evaluateWithObject:str2validate];
- }
(注册知乎)
我在之前说过知乎的登录模块在刚开始的时候也没有为邮箱输入框设置UIKeyboardTypeEmailAddress类型键盘,从注册界面我们也可以发现知乎改了登录界面,不过漏了这里。
或者,是故意地?
那么,不妨再比较一下默认中文键盘和邮箱键盘,仍然可以发现在这个注册流程中,还是邮箱键盘更方便。
(注册新浪微博)
新浪微博的邮箱注册界面仍然是一个webview,并且从加载出来的页面仍然可以感受到:新浪微博并不怎么为用户考虑!
为什么?
先不说那张验证码,就看下面两点:
- 需要用户点击按钮来发送激活邮件。当用户注册成功了为什么不自动发激活邮件呢?这让我想起前几天我第一次在万达官网上买电影票的经历,我输入了打折卡号和密码,网页上已经显示出折扣价了,于是我就继续支付,结果没有打折!!!因为我没有点击折扣价附近的“使用”按钮。那时候折扣价是红色字体突出的,吸引了我的注意力,而且我以前在其它网站买电影票都很顺利,有点大意。不过,如果真心为用户考虑,折扣价都已经算出来了,说明打折卡有效,为什么不默认使用呢!?事后,我打了好多个电话要投诉,都没人接。幸亏那次只能省10块钱。
- 需要查收激活邮件进行激活,才能登录。这点简直没有点移动App的意识嘛!我用手机注册,你还要我去邮箱查收激活邮件!?且不说你这是不是把手机当PC,就这注册流程,完全被一封激活邮件打断了!