开发中常见的问题

1.重复调用2次loadView和viewDidLoad

最好不要在UIViewController的loadView方法中改变状态栏的可视性(比如状态栏由显示变为隐藏、或者由隐藏变为显示),因为会导致重复调用2次loadView和viewDidLoad方法

假设状态栏本来是处于显示状态的:

下面的是错误代码:

复制代码
 1 - (void)loadView {
 2      NSLog(@"loadView");
 3      // 隐藏状态栏
 4      [UIApplication sharedApplication].statusBarHidden = YES;
 5      
 6      // .... 创建UIView
 7      self.view = [[[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds] autorelease];
 8      self.view.backgroundColor = [UIColor grayColor];
 9  }
10  
11  - (void)viewDidLoad {
12      [super viewDidLoad];
13      NSLog(@"viewDidLoad");
14  }
复制代码

运行效果:

打印信息:

1 2013-02-26 00:51:36.152 weibo[2251:c07] loadView  
2 2013-02-26 00:51:36.153 weibo[2251:c07] loadView  
3 2013-02-26 00:51:36.153 weibo[2251:c07] viewDidLoad  
4 2013-02-26 00:51:36.154 weibo[2251:c07] viewDidLoad

虽然运行效果是对的,但是系统连续调用了2次loadView和viewDidLoad方法,导致创建了2次UIView,造成了不必要的开销。

原因分析:

状态栏由显示变为隐藏,意味着屏幕的可用高度变长了,UIViewController的UIView的高度也要重新调整,因此系统会重新调用loadView方法创建UIView,创建完毕后再次调用viewDidLoad方法。

 

2.按钮无法点击

如果在UIImageView中添加了一个按钮,你会发现在默认情况下这个按钮是无法被点击的,需要设置UIImageView的userInteractionEnabled为YES:

imageView.userInteractionEnabled = YES;

设置为YES后,UIImageView内部的按钮就可以被点击了

原因分析:

• 当用户点击屏幕时,会产生一个触摸事件,系统会将该事件加入到一个由UIApplication管理的事件队列中

• UIApplication会从事件队列中取出最前面的事件进行分发以便处理,通常,先发送事件给应用程序的主窗口(UIWindow)

• 主窗口会调用hitTest:withEvent:方法在视图(UIView)层次结构中找到一个最合适的UIView来处理触摸事件

(hitTest:withEvent:其实是UIView的一个方法,UIWindow继承自UIView,因此主窗口UIWindow也是属于视图的一种)

• hitTest:withEvent:方法大致处理流程是这样的:

首先调用当前视图的pointInside:withEvent:方法判断触摸点是否在当前视图内:

▶ 若pointInside:withEvent:方法返回NO,说明触摸点不在当前视图内,则当前视图的hitTest:withEvent:返回nil

▶ 若pointInside:withEvent:方法返回YES,说明触摸点在当前视图内,则遍历当前视图的所有子视图(subviews),调用子视图的hitTest:withEvent:方法重复前面的步骤,子视图的遍历顺序是从top到bottom,即从subviews数组的末尾向前遍历,直到有子视图的hitTest:withEvent:方法返回非空对象或者全部子视图遍历完毕:

▷ 若第一次有子视图的hitTest:withEvent:方法返回非空对象,则当前视图的hitTest:withEvent:方法就返回此对象,处理结束

▷ 若所有子视图的hitTest:withEvent:方法都返回nil,则当前视图的hitTest:withEvent:方法返回当前视图自身(self)

• 最终,这个触摸事件交给主窗口的hitTest:withEvent:方法返回的视图对象去处理

我大致画了个iOS触摸事件分发的原理图:

• hitTest:withEvent:方法会忽略以下视图:

1> 隐藏(hidden=YES)的视图

2> 禁止用户操作(userInteractionEnabled=NO)的视图

3> alpha<0.01的视图

4> 如果一个子视图的区域超过父视图的区域(如果父视图的clipsToBounds属性为NO,超过父视图区域的子视图内容也会显示),那么正常情况下在父 视图区域外的触摸操作不会被识别,因为父视图的pointInside:withEvent:方法会返回NO,这样就不会继续向下遍历子视图了。当然,也 可以重写pointInside:withEvent:方法来处理这种

综上所述可得:如果父视图的userInteractionEnabled=NO,触摸事件不会继续往下传递给子视图,所以子视图永远无法处理触摸事件。而UIImageView在默认情况下的userInteractionEnabled就是NO。

 

3.@2x和-568h@2x

由于iOS设备的屏幕分辨率不尽相同,有大有小,那么在不同设备中 显示同一张图片,可能会造成图片被拉伸、变形,严重影响用户体验。

为了让图片在不同设备中都能得到很好的显示效果,同一类图片我们一般会准备3种版本,比如iOS程序在启动时会全屏显示的Default.png图片:

(Retina即视网膜屏幕)

• Default.png(图片尺寸为320x480):显示在非Retina-3.5英寸屏幕上(iPhone3G\iPhone3GS,屏幕分辨率为320x480)

• Default@2x.png(图片尺寸为640x960):显示在Retina-3.5英寸屏幕上(iPhone4\iPhone4s,屏幕分辨为640x960)

• Default-568h@2x.png(图片尺寸为640x1136):显示在Retina-4.0英寸屏幕上(iPhone5,屏幕分辨率为640x1136)

 

4.启动app时全屏显示Default.png

大部分app在启动过程中全屏显示一张背景图片,比如新浪微博会显示这张:

要想在iOS中实现这种效果,毫无压力,非常地简单,把需要全屏显示的图片命名为Default.png即可,在iOS app启动时默认会去加载并全屏显示Default.png。

也可以用其他名称来命名图片,在Info.plist配置一下即可:

配置过后,app启动时就会去加载并全屏显示lufy.png

在默认情况下,app显示Default.png时并非真正的"全屏显示",因为顶部的状态栏并没有被隐藏,比如下面的效果:

大部分情况下,我们都想隐藏状态栏,让Default.png真正全屏显示。

说到这里,可能有人马上就想到了一种办法:在AppDelegate的application:didFinishLaunchingWithOptions:方法中添加如下代码:

[UIApplication sharedApplication].statusBarHidden = YES; 

我只能说你的思路是对的,但实际上达不到想要的效果,你会发现显示Default.png时状态栏还是存在的,等Default.png显示完毕后,状态栏才被隐藏。

我先解释下为什么这种方法不可行,其实原因很简单:

1> Default.png是在app启动过程中加载的,并不是在app启动完毕后再加载的

2> AppDelegate的application:didFinishLaunchingWithOptions:方法是在app启动完毕后才调用的

下面说一下解决方案,在Info.plist中增加一个配置即可:

这里的YES表示在app初始化(启动)的时候就隐藏状态栏。

当然,在Default.png显示完毕后状态栏还是隐藏的。如果想重新显示状态栏,补上下面代码即可:

[UIApplication sharedApplication].statusBarHidden = NO; 

 

 
 一些小知识点:

SQL语句:

根据id来排序,删除表中的前3条数据
DELETE FROM sync WHERE id IN ( SELECT id FROM sync ORDER BY id LIMIT 3) 

 

 

1. viewDidUnload一般情况下只在memory warning的时候才被调用

2..PCH预编译头文件 在Xcode项目中是prefix.pch文件,在Supporting Files Group中使用预编译头文件是为了“提高编译速度” 把一个工程中较为稳定的、不经常修改的代码放在一个文件中,每次编译时如果预编译中的代码没有修改,那么就不再编译这部分代码;

 

3.PSCollectionView是一个特殊的tableView,布局称为瀑布流式布局,主要特征是错落有致,定宽而不定高,巧妙地利用了视觉层级,实现的任意流动又缓解了视觉疲劳。

4.NSString/NSArray/NSDictionary/NDData都可以直接与本地文件file交互,NSImage不可以,中间需要NSData转化一下。

5. 应用启动的时候调用

didFinishLaunchingWithOptions

applicationDidBecomeActive

    应用从前台进入后台时,会调用两个方法:

applicationWillResignActive

applicationDidEnterBackground

   应用从后台进入前台,会调用两个方法:

applicationWillEnterForeground

applicationDidBecomeActive

6.iphone开发国际化问题

http://blog.csdn.net/tangren03/article/details/8081032

7.sizeToFit  调用这个方法,会自动调节当前的view大小,例如在UIScrollView中嵌套UIWebView时,调用UIWebView的这个方法,就会自动把UIWebView的frame变大,不再滚动。

8.全局变量BOOL 的默认初始值是NO。局部变量需要初始化才可以使用,如果强制使用的话默认是YES。

 9.根据对象获取类名: [NSString stringWithUTF8String:object_getClassName(a)]; 获取对象的类名

 NSString *class_name = [NSStringstringWithUTF8String:object_getClassName(obj)];

10.通过类名来创建对象

Class testClass=NSClassFromString(@”testClass”);

id object=[[testClass alloc] init];

 

11.IsKindOfClass与IsMemberOfClass的区别:

IsKindOfClass用于判断是否是指定类的一个实例或者继承至指定类的子类的实例。

 

IsMemberOfClass用于判断是否是指定类的一个实例。只有在极少数特殊情况才起作用,不建议使用。
12.IsProxy若接受者继承至NSObject返回NO,其它都返回YES。

 

13.SEL可以看成是一个函数指针,通过函数名找到相应的方法,在编译时通过@selector()创建。

 

14.-(id)performSelector:(SEL)aSelector ;
-(id)performSelector:(SEL)aSelector withObject:(id)anObject ;
-(id)performSelector:(SEL)aSelector withObject:(id)anObject withObject:(id)anotherObjec ;
跟直接通过方法名调用类似,但通过消息发送通知的机制,效率更高,比满查找相应的类,找到对应的函数接口,再执行相应的代码。可以在类内使用,也可以在类的外部使用。
15.id与void *的区别:id指向一个未知类型的objective c对象,void *指向一个未知类型的对象,比id使用范围更广。
16.在我的代码中使用这段代码来屏蔽掉NSLog
//#define NSLog //NSLog

#define NSLog NSLog(@"#%s##%d#",strrchr(__FILE__,'/'),__LINE__);NSLog

#ifdef DEBUG

#define MYLog(f, ...) NSLog(f, ## __VA_ARGS__)

//#define DDLog(f, ...)

#else

//#define DDLog(f, ...) NSLog(f, ## __VA_ARGS__)

#define MYLog(f, ...)

#endif

 

17.

Architectures 项目里想要Xcode编译的目标设备

Build Active Architecture Only 属性设置为yes,为了debug时编译速度更快,只编译对应的版本 

 

posted @ 2016-04-12 16:45  超神船长  阅读(196)  评论(0编辑  收藏  举报