IOS开发中可能会遇到的那点事。。。(持续更新)

对于我的iOS开发成长之路,很早就想写一篇文章了,一方面是对这几年自己的iOS开发做一个总结,另一方面是对后来者有一定的借鉴意义。
15年初接触了Objective-C语言,从此踏上了IOS手机开发这条道路。。
恰逢公司这次安排我说说这几年手机的开发经验,借此机会特写下此文,整理下IOS开发的知识点,直接切入正题:

前期(都是一些基础知识点)

  1. 给navigation Bar 设置 title 颜色
UIColor *redColor = [UIColor redColor];
NSDictionary *dic = [NSDictionary dictionaryWithObject:redColor forKey:NSForegroundColorAttributeName];
[self.navigationController.navigationBar setTitleTextAttributes:dic];

2.修改textField的placeholder的字体颜色、大小

self.textField.placeholder = @"预留字!";
[self.textField setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"];
[self.textField setValue:[UIFont boldSystemFontOfSize:16] forKeyPath:@"_placeholderLabel.font"];

3.两点之间的距离

static __inline__ CGFloat CGPointDistanceBetweenTwoPoints(CGPoint point1, CGPoint point2)   
{
 CGFloat dx = point2.x - point1.x; CGFloat dy = point2.y - point1.y; return sqrt(dx*dx + dy*dy);
 }

4.关于iOS开发-关闭/收起键盘方法总结
(1)点击Return按扭时收起键盘

- (BOOL)textFieldShouldReturn:(UITextField *)textField 
{
    return [textField resignFirstResponder]; 
}

(2)点击背景View收起键盘(你的View必须是继承于UIControl)

[self.view endEditing:YES];

(3)统一收起键盘,可在任何地方加上这句话

[[[UIApplication sharedApplication] keyWindow] endEditing:YES];

5.如何把一个CGPoint存入数组里

CGPoint  itemSprite1position = CGPointMake(100, 100);
NSMutableArray * array  = [[NSMutableArray alloc] initWithObjects:NSStringFromCGPoint(itemSprite1position),nil];
    //    从数组中取值的过程是这样的:   
CGPoint point = CGPointFromString([array objectAtIndex:0]);

NSLog(@"point is %@.", NSStringFromCGPoint(point));

6.UIPickerView 判断开始选择到选择结束
开始选择的,需要在继承UiPickerView,创建一个子类,在子类中重载:

- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event

当[super hitTest:point withEvent:event]返回不是nil的时候,说明是点击中UIPickerView中了。
结束选择的, 实现UIPickerView的delegate方法:

- (void)pickerView:(UIPickerView*)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component

调用这个方法的时候,说明选择已经结束了。

7.iOS模拟器 键盘事件
iOS模拟器 选择了Keybaord->Connect Hardware keyboard 后,不弹出键盘。
当代码中添加了

[[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillHide)
                                                 name:UIKeyboardWillHideNotification
                                               object:nil];

进行键盘事件的获取。那么在此情景下将不会调用- (void)keyboardWillHide。
因为没有键盘的隐藏和显示。

8.使用UIScrollViewKeyboardDismissMode实现了Message app的行为

像Messages app一样在滚动的时候可以让键盘消失是一种非常好的体验。然而,将这种行为整合到你的app很难。幸运的是,苹果给UIScrollView添加了一个很好用的属性keyboardDismissMode,这样可以方便很多。
现在仅仅只需要在Storyboard中改变一个简单的属性,或者增加一行代码,你的app可以和办到和Messages app一样的事情了。
这个属性使用了新的UIScrollViewKeyboardDismissMode enum枚举类型。这个enum枚举类型可能的值如下:

typedef NS_ENUM(NSInteger, UIScrollViewKeyboardDismissMode) {
    UIScrollViewKeyboardDismissModeNone,
    UIScrollViewKeyboardDismissModeOnDrag,      // dismisses the keyboard when a drag begins
    UIScrollViewKeyboardDismissModeInteractive, // the keyboard follows the dragging touch off screen, and may be pulled upward again to cancel the dismiss
} NS_ENUM_AVAILABLE_IOS(7_0);
  1. statusbar
    (1)文字颜色
    iOS7上,默认status bar字体颜色是黑色的,要修改为白色的需要在infoPlist里设置UIViewControllerBasedStatusBarAppearance为NO,然后在代码里添加:
[application setStatusBarStyle:UIStatusBarStyleLightContent];

(2)设置Status bar颜色
如果没有navigation bar, 直接设置

self.view.backgroundColor = COLOR_APP_MAIN;

如果有navigation bar, 在navigation bar 添加一个view来设置颜色。


UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, -20, ScreenWidth, 20)];
[view setBackgroundColor:COLOR_APP_MAIN];

[viewController.navigationController.navigationBar addSubview:view];

10.将color转为UIImage

//将color转为UIImage
- (UIImage *)createImageWithColor:(UIColor *)color
{
    CGRect rect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f);
    UIGraphicsBeginImageContext(rect.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetFillColorWithColor(context, [color CGColor]);
    CGContextFillRect(context, rect);
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return newImage;
}

11.NSTimer 用法

NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:.02 target:self selector:@selector(tick:) userInfo:nil repeats:YES];

    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
    //在NSRunLoop 中添加定时器.

12.NSDate 获取几年前的时间

//获取10年前时间
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *dateComponents = [[NSDateComponents alloc] init];
[dateComponents setYear:-10];
self.birthDate = [gregorian dateByAddingComponents:dateComponents toDate:[NSDate date] options:0];

13.iOS加载启动图的时候隐藏statusbar

只需需要在info.plist中加入Status bar is initially hidden 设置为YES就好

14.iOS7 中 boundingRectWithSize: options: attributes: context: 计算文本尺寸的使用
之前使用了NSString类的sizeWithFont:constrainedToSize:lineBreakMode:方法,但是该方法已经被iOS7 Deprecated了,而iOS7新出了一个boudingRectWithSizeptions:attributes:context方法来代替。
而具体怎么使用呢,尤其那个attribute

NSDictionary *attribute = @{NSFontAttributeName: [UIFont systemFontOfSize:14]};
CGSize size = [@"相关NSString" boundingRectWithSize:CGSizeMake(100, 0) options: NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:attribute context:nil].size;

15.iOS 开发,工程中混合使用 ARC 和非ARC
Xcode 项目中我们可以使用 ARC 和非 ARC 的混合模式。
如果你的项目使用的非 ARC 模式,则为 ARC 模式的代码文件加入 -fobjc-arc 标签。
如果你的项目使用的是 ARC 模式,则为非 ARC 模式的代码文件加入 -fno-objc-arc 标签。
添加标签的方法:
打开:你的target -> Build Phases -> Compile Sources.
双击对应的 *.m 文件
在弹出窗口中输入上面提到的标签 -fobjc-arc / -fno-objc-arc
点击保存

16.在UIViewController中property的一个UIViewController的Present问题
如果在一个UIViewController A中有一个property属性为UIViewController B,实例化后,将BVC.view 添加到主UIViewController A.view上,如果在viewB上进行 - (void)presentViewController: (UIViewController *)viewControllerToPresent animated: (BOOL)flag completion:(void (^)(void))completion NS_AVAILABLE_IOS(5_0);的操作将会出现,“ Presenting view controllers on detached view controllers is discouraged ” 的问题。
以为BVC已经present到AVC中了,所以再一次进行会出现错误。
可以使用以下代码解决;

[self.view.window.rootViewController presentViewController:imagePicker
                                                      animated:YES
                                                    completion:^{
                                                        NSLog(@"Finished");
                                                    }];

17.UITableViewCell indentationLevel 使用
UITableViewCell 属性 NSInteger indentationLevel 的使用, 对cell设置 indentationLevel的值,可以将cell 分级别。
还有 CGFloat indentationWidth; 属性,设置缩进的宽度。
总缩进的宽度: indentationLevel * indentationWidth

18.ActivityViewController 使用AirDrop分享
使用以下代码即可弹出airdrop界面

NSArray *array = @[@"test1", @"test2"];

UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:array applicationActivities:nil];

[self presentViewController:activityVC animated:YES
                 completion:^{
                     NSLog(@"Air");
                 }];

19.APP 屏蔽 触发事件

[[UIApplication sharedApplication] beginIgnoringInteractionEvents];

20.NSDictionary 转 NSString

//将dictionary 转化为 NSData, data 转化为 string .

NSDictionary *parametersDic = [NSDictionary dictionaryWithObjectsAndKeys:
self.providerStr, KEY_LOGIN_PROVIDER,
token, KEY_TOKEN,
response, KEY_RESPONSE,
nil];

NSData jsonData = parametersDic == nil ? nil : [NSJSONSerialization dataWithJSONObject:parametersDic options:0 error:nil];
NSString requestBody = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];

21.User-Agent 判断设备
UIWebView 会根据User-Agent 的值来判断需要显示哪个界面。
如果需要设置为全局,那么直接在应用启动的时候加载。

(void)appendUserAgent
{
NSString oldAgent = [self.WebView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];
NSString newAgent = [oldAgent stringByAppendingString:@"iOS"];//@“iOS" 为添加的自定义。


NSDictionary *dic = [[NSDictionary alloc] initWithObjectsAndKeys:

                   newAgent, @"UserAgent", nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:dic];
}

22.class_addMethod 使用

//当 ARC 环境下,使用的时候@selector 需要使用super的class,不然会报错。
class_addMethod([self class], @selector(resolveThisMethodDynamically), (IMP) myMethodIMP, "v@:");

//当MRC下,可以任意定义。但是系统会出现警告,忽略警告就可以。
class_addMethod([EmptyClass class], @selector(sayHello2), (IMP)sayHello, "v@:");

23.非空判断注意

//如果进行非空判断和类型判断时,需要新进行类型判断,再进行非空判断,不然会crash。
BOOL hasBccCode = YES;
if ( nil == bccCodeStr
    || [bccCodeStr isKindOfClass:[NSNull class]]
    || [bccCodeStr isEqualToString:@""])
{
    hasBccCode = NO;
}

24.iOS 8 UIAlertView 键盘显示问题
在调用UIAlertView 之前进行键盘是否已经隐藏的判断。

@property (nonatomic, assign) BOOL hasShowdKeyboard;

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(showKeyboard)
                                             name:UIKeyboardWillShowNotification
                                           object:nil];

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(dismissKeyboard)
                                             name:UIKeyboardDidHideNotification
                                           object:nil];

- (void)showKeyboard
{
    self.hasShowdKeyboard = YES;
}

- (void)dismissKeyboard
{
    self.hasShowdKeyboard = NO;
}

while ( self.hasShowdKeyboard )
{
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}

UIAlertView* alerview = [[UIAlertView alloc] initWithTitle:@"" message:@"取消修改?" delegate:self cancelButtonTitle:@"取消" otherButtonTitles: @"确定", nil];
[alerview show];

25.UIView 自带动画翻转界面

- (IBAction)changeImages:(id)sender
{
    CGContextRef context = UIGraphicsGetCurrentContext();

    [UIView beginAnimations:nil context:context];
    [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
    [UIView setAnimationDuration:1.0];

    [UIView setAnimationTransition:UIViewAnimationTransitionCurlDown forView:_parentView cache:YES];
    [UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:_parentView cache:YES];
    [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:_parentView cache:YES];
    [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:_parentView cache:YES];

    NSInteger purple = [[_parentView subviews] indexOfObject:self.image1];
    NSInteger maroon = [[_parentView subviews] indexOfObject:self.image2];

    [_parentView exchangeSubviewAtIndex:purple withSubviewAtIndex:maroon];

    [UIView setAnimationDelegate:self];
    [UIView commitAnimations];


}

26.KVO 监听其他类的变量

[[HXSLocationManager sharedManager] addObserver:self
                                         forKeyPath:@"currentBoxEntry.boxCodeStr"
                                            options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionOld context:nil];

27.iOS9 crash animateWithDuration
在iOS9 中,如果进行animateWithDuration 时,view被release 那么会引起crash。

会crash代码:

[UIView animateWithDuration:0.25f animations:^{
        self.frame = selfFrame;
    } completion:^(BOOL finished) {
        if (finished) {
            [super removeFromSuperview];
        }
    }];

不会crash代码:

[UIView animateWithDuration:0.25f
                          delay:0
         usingSpringWithDamping:1.0
          initialSpringVelocity:1.0 options:UIViewAnimationOptionCurveLinear
                     animations:^{
                         self.frame = selfFrame;
                     } completion:^(BOOL finished) {
                         [super removeFromSuperview];
                     }];

28.对NSString进行URL编码转换
对于URL中有中文字符的情况,需对URL进行编码转换。

urlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

29.保存全屏为image

CGSize imageSize = [[UIScreen mainScreen] bounds].size;
UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();

for (UIWindow * window in [[UIApplication sharedApplication] windows]) {
    if (![window respondsToSelector:@selector(screen)] || [window screen] == [UIScreen mainScreen]) {
        CGContextSaveGState(context);
        CGContextTranslateCTM(context, [window center].x, [window center].y);
        CGContextConcatCTM(context, [window transform]);
        CGContextTranslateCTM(context, -[window bounds].size.width*[[window layer] anchorPoint].x, -[window bounds].size.height*[[window layer] anchorPoint].y);
        [[window layer] renderInContext:context];

        CGContextRestoreGState(context);
    }
}

UIImage *image = UIGraphicsGetImageFromCurrentImageContext();

30.判断定位状态 locationServicesEnabled
这个[CLLocationManager locationServicesEnabled]检测的是整个iOS系统的位置服务开关,无法检测当前应用是否被关闭。通过CLAuthorizationStatus来判断是否可以访问GPS

CLAuthorizationStatus status = [CLLocationManager authorizationStatus];
    if (kCLAuthorizationStatusDenied == status || kCLAuthorizationStatusRestricted == status) {
        [self locationManager:self.locationManager didUpdateLocations:nil];
    } else { // the user has closed this function
        [self.locationManager startUpdatingLocation];
    }

31.微信分享的时候注意大小
text 的大小必须 大于0 小于 10k

image 必须 小于 64k

url 必须 大于 0k

32.图片缓存的清空
一般使用SDWebImage 进行图片的显示和缓存,一般缓存的内容比较多了就需要进行清空缓存
清除SDWebImage的内存和硬盘时,可以同时清除session 和 cookie的缓存。

// 清理内存
[[SDImageCache sharedImageCache] clearMemory];

// 清理webview 缓存
NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (NSHTTPCookie *cookie in [storage cookies]) {
    [storage deleteCookie:cookie];
}

NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
[config.URLCache removeAllCachedResponses];
[[NSURLCache sharedURLCache] removeAllCachedResponses];

// 清理硬盘
[[SDImageCache sharedImageCache] clearDiskOnCompletion:^{
    [MBProgressHUD hideAllHUDsForView:self.view animated:YES];

    [self.tableView reloadData];
}];

33.TableView Header View 跟随Tableview 滚动
当tableview的类型为 plain的时候,header View 就会停留在最上面。

当类型为 group的时候,header view 就会跟随tableview 一起滚动了。

34.UITabBar,移除顶部的阴影

[[UITabBar appearance] setShadowImage:[[UIImage alloc] init]];
[[UITabBar appearance] setBackgroundImage:[[UIImage alloc] init]];

35.JSON的“” 转换为nil

AFJSONResponseSerializer *response = [[AFJSONResponseSerializer alloc] init];
response.removesKeysWithNullValues = YES;
_sharedClient.responseSerializer = response;

36.解决cell重用的问题
UITableView通过重用单元格来达到节省内存的目的,通过为每个单元格指定一个重用标示(reuseidentifier),即指定了单元格的种类,以及当单元格滚出屏幕时,允许恢复单元格以便复用。对于不同种类的单元格使用不同的ID,对于简单的表格,一个标示符就够了。
如一个TableView中有10个单元格,但屏幕最多显示4个,实际上iPhone只为其分配4个单元格的内存,没有分配10个,当滚动单元格时,屏幕内显示的单元格重复使用这4个内存。实际上分配的cell的个数为屏幕最大显示数,当有新的cell进入屏幕时,会随机调用已经滚出屏幕的Cell所占的内存,这就是Cell的重用。
对于多变的自定义Cell,这种重用机制会导致内容出错,为解决这种出错的方法,把原来的

UITableViewCell *cell = [tableview dequeueReusableCellWithIdentifier:defineString]

修改为:

UITableViewCell *cell = [tableview cellForRowAtIndexPath:indexPath];

这样就解决掉cell重用机制导致的问题。

37.一个view已经初始化完毕,view上面添加了n个button,除用view的tag之外,还可以采用什么办法来找到自己想要的button来修改button的值

有2种方法解决:

第一种:如果是点击某个按钮后,才会刷新它的值,其它不用修改,那么不用引用任何按钮,直接在回调时,就已经将接收响应的按钮给传过来了,直接通过它修改即可。
第二种:点击某个按钮后,所有与之同类型的按钮都要修改值,那么可以通过在创建按钮时将按钮存入到数组中,在需要的时候遍历查找。

38.对于Run Loop的理解
RunLoop,是多线程的法宝,即一个线程一次只能执行一个任务,执行完任务后就会退出线程。主线程执行完即时任务时会继续等待接收事件而不退出。非主线程通常来说就是为了执行某一任务的,执行完毕就需要归还资源,因此默认是不运行RunLoop的;
每一个线程都有其对应的RunLoop,只是默认只有主线程的RunLoop是启动的,其它子线程的RunLoop默认是不启动的,若要启动则需要手动启动;
在一个单独的线程中,如果需要在处理完某个任务后不退出,继续等待接收事件,则需要启用RunLoop;
NSRunLoop提供了一个添加NSTimer的方法,可以指定Mode,如果要让任何情况下都回调,则需要设置Mode为Common模式;
实质上,对于子线程的runloop默认是不存在的,因为苹果采用了懒加载的方式。如果我们没有手动调用[NSRunLoop currentRunLoop]的话,就不会去查询是否存在当前线程的RunLoop,也就不会去加载,更不会创建。

39.将字符串“2016-04-10”格式化日期转为NSDate类型

NSString *timeStr = @"2016-04-10";
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = @"yyyy-MM-dd";
formatter.timeZone = [NSTimeZone defaultTimeZone];
NSDate *date = [formatter dateFromString:timeStr];
NSLog(@"%@", date);

40.内存的使用和优化的注意事项
重用问题:如UITableViewCells、UICollectionViewCells、UITableViewHeaderFooterViews设置正确的reuseIdentifier,充分重用;
尽量把views设置为不透明:当opque为NO的时候,图层的半透明取决于图片和其本身合成的图层为结果,可提高性能;
不要使用太复杂的XIB/Storyboard:载入时就会将XIB/storyboard需要的所有资源,包括图片全部载入内存,即使未来很久才会使用。那些相比纯代码写的延迟加载,性能及内存就差了很多;
选择正确的数据结构:学会选择对业务场景最合适的数组结构是写出高效代码的基础。比如,数组: 有序的一组值。使用索引来查询很快,使用值查询很慢,插入/删除很慢。字典: 存储键值对,用键来查找比较快。集合: 无序的一组值,用值来查找很快,插入/删除很快。
gzip/zip压缩:当从服务端下载相关附件时,可以通过gzip/zip压缩后再下载,使得内存更小,下载速度也更快。
延迟加载:对于不应该使用的数据,使用延迟加载方式。对于不需要马上显示的视图,使用延迟加载方式。比如,网络请求失败时显示的提示界面,可能一直都不会使用到,因此应该使用延迟加载。
数据缓存:对于cell的行高要缓存起来,使得reload数据时,效率也极高。而对于那些网络数据,不需要每次都请求的,应该缓存起来,可以写入数据库,也可以通过plist文件存储。
处理内存警告:一般在基类统一处理内存警告,将相关不用资源立即释放掉
重用大开销对象:一些objects的初始化很慢,比如NSDateFormatter和NSCalendar,但又不可避免地需要使用它们。通常是作为属性存储起来,防止反复创建。
避免反复处理数据:许多应用需要从服务器加载功能所需的常为JSON或者XML格式的数据。在服务器端和客户端使用相同的数据结构很重要;
使用Autorelease Pool:在某些循环创建临时变量处理数据时,自动释放池以保证能及时释放内存;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值