iOS

限制最大输入字符数(更新)

在 这篇博客 中提到的限制 UITextView 最大输入字符数的方法,依然是有问题的。问题出现于用拼音输入法输入中文的时候,比如键入“你好世界”,对应的拼音为“nihaoshijie”。限制最大长度为 5,则虽然“你好世界”为四个字,但在输入拼音字符的时候,会超过 5 个字符,导致无法输入进去。

iOS:UITextField中文输入法输入时对字符长度的限制 这篇文章中提出了一个方法,经尝试是可以正确处理这种情形的。基本方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- (void)textViewDidChange:(UITextView *)textView {
NSString *lang = [[textView textInputMode] primaryLanguage];
// 对简体中文输入进行特殊处理
if ([lang isEqualToString:@"zh-Hans"]) {
UITextRange *selectedRange = [textView markedTextRange];
// 获取高亮部分
UITextPosition *position = [textView positionFromPosition:selectedRange.start offset:0];
if (!position) {
// 没有高亮选择的字,则进行字数限制
if (textView.text.length > LENGTH_LIMIT) {
textView.text = [textView.text substringToIndex:LENGTH_LIMIT];
}
} else{
// 有高亮选择的字,认为现在正在拼音输入状态,暂不进行限制
}
} else {
// 简体中文以外的直接限制,暂不考虑其他语言
if (textView.text.length > LENGTH_LIMIT) {
textView.text = [textView.text substringToIndex:LENGTH_LIMIT];
}
}
}

同时,在上面这篇文章中还受到一点启示,UITextField 是没有类似 textViewDidChange 这样的代理方法的,所以对于 UITextField 我们该怎么办?可以通过监听 UITextFieldTextDidChangeNotification 通知来达到一样的目的。

在初始化时添加监听:

1
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textFieldDidChanged:) name:@"UITextFieldTextDidChangeNotification" object:_textField];

实现监听方法:

1
2
3
4
-(void)textFieldDidChanged:(NSNotification *)notification {
UITextField *textField = (UITextField*)notification.object;
...
}

别忘了在 dealloc 时移除监听:

1
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"UITextFieldTextDidChangeNotification" object:_textField];

UIAlertView without title

UIAlertView 是支持无标题的。如果传空字符串,即为没有标题,只显示内容;如果传 nil,则会把内容放到标题上。

1、title 传空字符串,即:

1
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"" message:@"您确定要退出此帐号吗?" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"确认", nil];

2、title 传 nil,即:

1
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:nil message:@"您确定要退出此帐号吗?" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"确认", nil];

另外,如果需要自定义 AlertView 的样式,可以采用开源的 LGAlertView。

MBProgressHUD and UITableView section header

在一个 UITableView 上添加一个 MBProgressHUD,很有可能会遇到这种情况,HUD 被 TableView 的 section header 挡住了:

同样的,如果你用了 UITableViewController,则 self.view 就是一个 UITableView,则当向 self.view 添加一个 HUD 为 subview 时,就会遇到这个问题。

在 github 上的 issue:
https://github.com/jdg/MBProgressHUD/issues/242

在 stackoverflow 上的讨论:
http://stackoverflow.com/questions/7199700/uitableview-headings-shown-on-top-of-mbprogresshud

可以根据情况考虑,选择下面的方法:

  1. 不用 UITableViewController,改用 UIViewController + TableView,并把 hud 添加到 self.view 上。
  2. 把 hud 添加到 self.navigationController.view 上,这样会带来一个副作用,hud 显示的时候不能点击返回按钮,不推荐。
  3. 调整 hud 的 z 值,hud.layer.zPosition = MAXFLOAT; (z 值越大越在上方),推荐采用。

限制 UITextView 最大输入字符数

实现以下两个 UITextView 的代理方法即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
#define LENGTH_LIMIT 10
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
if (textView.text.length >= LENGTH_LIMIT && text.length > 0) {
return NO;
}
return YES;
}
-(void)textViewDidChange:(UITextView *)textView {
if (textView.text.length > LENGTH_LIMIT) {
textView.text = [textView.text substringToIndex:LENGTH_LIMIT];
}
}

注意,这两个代理方法必须都实现。

如果只实现第一个代理方法,则会出现一个问题:到达字数限制时,用键盘的确已无法输入进去,但可以通过点击键盘上方的联想字词继续输入进去,导致超过限制。通过实现第二个代理方法,可把通过联想或其他方式输入进去的多余字符截掉。这也是之前我没有注意到的一点。

如果只实现第二个代理方法,虽然长度不会超过限制,但实际相当于先输入进去,后再截掉,导致按下键盘后虽然没有输入进去字符,但键盘上方的联想字词在不断地根据你的按键而联想。

iOS 8.2 UITableView 的默认行高 bug

Cell高度错误

很简单的一个登录页面,用 Storyboard 的 Static Cell 来实现。开发时没有发现任何问题,交予测试时反馈输入框太小了,就是上面截图的样子,现象 100% 出现。而且发现,从该页面 Push 到下一层页面再 Pop 回该页面后,高度即可恢复正常。

检查程序没发现任何问题,Cell Height 都采用了默认高度。在我的模拟器上和其他测试设备上也无法重现这个问题。

检查那台出问题的测试机,发现系统版本为 iOS 8.2,而我的模拟器和测试机上都运行着 iOS 8.3 或 iOS 7.1。于是安装 iOS 8.2 模拟器,问题在 8.2 版本的模拟器上重现了!

经研究,推测为 iOS 8.2 的系统 bug,只要是采用了 TableView 的默认行高,就会出现行高不正确的问题。这个 bug 已经在 iOS 8.3 上得到修复。若要确保 iOS 8.2 系统下也不会出现显示异常,可以采用以下任意一种方式:

  1. 把 TableView 的 Row Height 属性设置为不是默认 44 的值。
  2. 把 TableView 中的 TableViewCell 的 Row Height 属性,选中 Custom。

Pop then push immediately with UINavigationController in iOS 8

在某些业务场景下,需要将一个 ViewController 从 NavigationController 里 pop 后,立即 push 进另一个 ViewController。在 iOS8 之前这不是什么问题,用以下代码即可轻松实现:

1
2
[navigationController popViewControllerAnimated:NO];
[navigationController pushViewController:newViewController animated:YES];

但在 iOS8 上这样无法实现,pop 后不会 push 另一个 ViewController。可以用以下方法实现,思路是直接修改 navigationController 里栈的数据(暂时没有找到其他更好的方法):

1
2
3
4
NSMutableArray *viewControllersInStack = [NSMutableArray arrayWithArray:navigationController.viewControllers];
[viewControllersInStack removeLastObject];
[viewControllersInStack addObject:newViewController];
[navController setViewControllers:viewControllersInStack animated:YES];

iOS8 中 UILabel 不显示 subview 的问题

在 iOS8 中,如果在一个 UILabel 对象上添加 subview,同时给这个 label 设置了背景色 (backgroundColor),则会发现这些 subview 都不显示出来,但可以响应事件(如一个按钮作为 subview,按钮不显示但可以正常响应点击)。如果不给这个 label 设置背景色,则可以正常显示。

在 iOS7 上不存在此问题。

有些开发者为了方便,在自定义 NavigationBar 时用 UILabel 做父容器,然后在其上添加按钮,导致了在 iOS8 上运行时,导航栏上的按钮都不显示。

避免采用 UILabel 等做父容器,它们本来就不适合做父容器。

Cannot locate compiling with Xcode6

正式推出的 iOS8 中,定位服务分为了 使用应用程序期间 和 始终 两种权限,这导致了一些变化。之前的工程若用 Xcode5 编译,并在 iOS8 上运行,会向用户请求始终获取位置的权限。若用 Xcode6 编译,会发现程序不会向用户请求权限,同时导致无法定位。

解决方法并不复杂,只需在 Info.plist 中加入请求定位权限时显示给用户的提示信息即可:

-Info.plist
1
2
<key>NSLocationWhenInUseUsageDescription</key>
<string>写上为什么要在程序运行时获取位置</string>

或

-Info.plist
1
2
<key>NSLocationAlwaysUsageDescription</key>
<string>写上为什么要始终获取位置</string>

iOS 代码规范

这是我为团队制定的 iOS 代码规范,供大家参考。

文档说明

Version Time Author
0.1 Alpha 2014-08-21 StoneArk
0.2 Beta 2014-09-02 StoneArk
0.3 Beta 2014-09-15 StoneArk
1.0 2014-09-16 StoneArk

基本原则

求同存异。

合理的编码风格不只一个,尊重每位开发人员的编码习惯。但不合理的编码习惯必须避免。

命名规则

  • 命名规则
范围 命名规则 示例
变量名、方法名 驼峰式命名 (小驼峰式命名) onlineCount
- (void)viewDidLoad
类名、枚举类型名 Pascal 命名 MainViewController
FruitType
常量 全部大写或k开头的驼峰式命名 FONTNAME
kFontName
宏定义 全部大写 LOCALDEBUG
枚举值 要有枚举类型前缀 FruitType 枚举类型的枚举值应为 FruitTypeApple, FruitTypeBanana 等
  • 命名约定

iOS 8 Glance

把自己的 4S 升级到了 iOS 8 GM Seed。第一次恢复了 iOS 7 上做的备份,结果卡得要命。然后重新刷了一遍,并设置为全新的 iPhone,流畅了许多,流畅度跟 iOS 7 差距不大。

Home Screen,多了个健康: