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,多了个健康: