lazyyang 2015-01-16T03:41:30+00:00 ccf.developer@gmail.com UIWindow 2015-01-15T00:00:00+00:00 lazyyang /2015/01/15/uiwindow 大多数人对于UIWindow都很熟悉,但又很不了解,这篇文章我们就谈谈UIWindow。UIWindow继承与UIView,我们创建的第一个视图控件就是UIWindow。

KeyWindow

一个APP可以拥有很多UIWindow,但是同一时间同一时刻,永远只能有一个KeyWindow,keyWindow是指定的用来接收键盘和其它非触摸事件。UIWindow创建出来默认是隐藏的,我们可以通过makeKeyAndVisible让其成为KeyWindow,该属性同时也将其hidden属性设置为NO。也可以分别设置,例如

1 [self.window makeKeyWindow];
2 self.window.hidden = NO;

如何创建一个UIWindow

先看看下面这段代码

1 - (void)btnClicked:(UIButton *)button
2 {
3     UIWindow *window = [[UIWindow alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
4     window.backgroundColor = [UIColor redColor];
5     [window makeKeyAndVisible];
6 }

分析:当我们点击UIButton,出发该事件。但是我们发现,UIWindow没有生成,这是为什么呢?原因就在window创建在栈里,会很快被释放。正确的写法应该是这样,创建一个UIWindow的属性,如下,

1 -(void)btnClicked:(UIButton *)button
2 {
3     _window = [[UIWindow alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
4     self.window.backgroundColor = [UIColor redColor];
5     [self.window makeKeyAndVisible];
6 }

UIWindow的方向
让我们看下面一个简单的例子,

1 -(void)viewWillLayoutSubviews
2 {
3     UIWindow *window = [[UIApplication sharedApplication] keyWindow];   
4     if (UIInterfaceOrientationIsLandscape(self.interfaceOrientation)){
5         NSLog(@"window of LandsCape:%@",NSStringFromCGRect(window.frame));
6     } else{
7         NSLog(@"window of Portrait:%@",NSStringFromCGRect(window.frame));
8     }
9 }

iOS7模拟器打印结果:
2015-01-15 17:52:52.120 UIWindowDemo[67037:613] window of Portrait:{{0, 0}, {320, 568}}
2015-01-15 17:54:08.702 UIWindowDemo[67037:613] window of LandsCape:{{0, 0}, {320, 568}}

iOS8模拟器中打印结果:
2015-01-15 17:58:01.223 UIWindowDemo[67131:2455317] window of Portrait:{{0, 0}, {320, 568}}
2015-01-15 17:58:24.281 UIWindowDemo[67131:2455317] window of LandsCape:{{0, 0}, {568, 320}}

分析:从中我们可以看出,在iOS7中,不管手机是横屏状态还是竖屏状态,UIWindow总是Portrait方向。而iOS8中,UIWindow的方向与设备方向保持一致。

KeyBoard Window

KeyBoard是一个window,在介绍keyBoard之前我们先看下面这个简单的例子:

 1 -(void)btnClicked:(UIButton *)button
 2 {
 3     NSArray *array = [[UIApplication sharedApplication] windows];
 4     NSLog(@"array two = %@",array);
 5 }
 6 
 7 -(void)viewDidLoad {
 8     [super viewDidLoad];
 9     NSArray *array = [[UIApplication sharedApplication] windows];
10     NSLog(@"arrayOne = %@",array);
11     UIButton *btnOne = [UIButton buttonWithType:UIButtonTypeRoundedRect];
12     btnOne.frame = CGRectMake(0, 0, 200, 200);
13     [btnOne setTitle:@"Clicked me" forState:UIControlStateNormal];
14     [btnOne addTarget:self action:@selector(btnClicked:) forControlEvents:UIControlEventTouchUpInside];
15     [self.view addSubview:btnOne]; 
16      UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(0, 300, 300, 100)];
17     textField.placeholder = @"Please write here";
18     [self.view addSubview:textField];
19 }

注意:再点击Button事件之前,我们最好触摸下UITextField,从而触发键盘时间。
点击Button事件之前打印: 2015-01-16 09:45:38.827 UIWindowDemo[70940:2531924] arrayOne = ( "<UIWindow: 0x7fc882d32a60; frame = (0 0; 375 667); gestureRecognizers = <NSArray: 0x7fc882d332a0>; layer = <UIWindowLayer: 0x7fc882d305a0>>" )

点击Button事件之后打印: 2015-01-16 09:47:07.851 UIWindowDemo[70940:2531924] array two = ( "<UIWindow: 0x7fc882d32a60; frame = (0 0; 375 667); gestureRecognizers = <NSArray: 0x7fc882d332a0>; layer = <UIWindowLayer: 0x7fc882d305a0>>", "<UITextEffectsWindow: 0x7fc882c30d20; frame = (0 0; 375 667); opaque = NO; gestureRecognizers = <NSArray: 0x7fc882c3ef60>; layer = <UIWindowLayer: 0x7fc882c13a50>>" )

分析:我们从中可以看出,当点击了Button事件后,画了一个UITextField。同时[[UIApplication sharedApplication] windows]中新添加了一个UITextEffectsWindow,而这个UITextEffectsWindow就是KeyBoard Window。如我们前面所说,在iOS7系统以及之前的系统中,UIWindow的坐标系永远是portrait方向,而iOS8中,UIWindow的坐标系与手机方向保持一致。那么键盘也是一个UIWindow,是不是也满足这个条件呢?下面我们观察下键盘的打印结果:

iOS7中,
Portrait方向:
2015-01-16 10:00:33.189 UIWindowDemo[71088:613] UITextEffectsWindow:<UITextEffectsWindow: 0x7f9631460fa0; frame = (0 0; 320 568); opaque = NO; gestureRecognizers = <NSArray: 0x7f96314618a0>; layer = <UIWindowLayer: 0x7f9631437600>>

Landscape方向:
2015-01-16 10:01:02.592 UIWindowDemo[71088:613] UITextEffectsWindow:<UITextEffectsWindow: 0x7f9631460fa0; frame = (0 0; 320 568); transform = [0, -1, 1, 0, -124, 124]; opaque = NO; gestureRecognizers = <NSArray: 0x7f96314618a0>; layer = <UIWindowLayer: 0x7f9631437600>>

iOS8中,
Portrait方向:
UITextEffectsWindow:<UITextEffectsWindow: 0x7fe621f49410; frame = (0 0; 320 568); opaque = NO; gestureRecognizers = <NSArray: 0x7fe621f1f490>; layer = <UIWindowLayer: 0x7fe621f1bbe0>>

Landscape方向: 2015-01-16 10:07:49.309 UIWindowDemo[71130:2542897] UITextEffectsWindow:<UITextEffectsWindow: 0x7fe621f49410; frame = (0 0; 568 320); opaque = NO; gestureRecognizers = <NSArray: 0x7fe621f1f490>; layer = <UIWindowLayer: 0x7fe621f1bbe0>>

从上面的打印结果我们可以看出,在iOS7中,keyBorad Window的方向也永远是Portrait的,当KeyBoard旋转的时候,系统会给KeyBoard一个transform的旋转变换。

Status Bar

Status Bar也是一个window,但是windows数组本身是不包含statusbar window。statusbar window是UIStatusBarWindow类型,我们可以通过下面这个方式来获取这个window

1 - (UIWindow *)statusWindow
2 {
3     NSString *statusBarString = [NSString stringWithFormat:@"_statusBarWindow"];
4     return [[UIApplication sharedApplication] valueForKey:statusBarString];
5 }

跟KeyBoard window类似,在iOS7中,statusbar frame同样不会改变。当你旋转设备的时候,系统会对statusbar进行transform旋转变换。但是注意,在iOS8 LandScape的情况下,status bar是被隐藏的.

1 CGRect rect = [UIApplication sharedApplication].statusBarFrame;  
2 NSLog(@"statusBarFrame %@", NSStringFromCGRect(rect));

我们可以看下面这个iOS8 Landscape的打印结果: 2015-01-16 11:29:19.235 UIWindowDemo[71511:2578866] statusFrame={{0, 320}, {0, 0}}

在iOS8 Landscape的情况下,Landscape的长和宽都为零,故默认是不显示出来的

]]>
序列化与反序列化 2014-06-26T07:59:06+00:00 lazyyang /2014/06/26/xu-lie-hua-yu-fan-xu-lie-hua 归档即序列化与反序列化

NSCoding是一个协议,主要是下面两个方法

  • -(id)initWithCoder:(NSCoder *)coder;//从coder中读取数据,保存到相应的变量中,即反序列化数据

  • -(void)encoderWithCoder:(NSCoder *)coder;//读取实例变量,并把这些数据写到coder中去。

NSCoder是一个抽象类,抽象类不能被实例化,只能提供一些想让子类继承的方法
NSKeyedUnarchiver 从二进制流读取对象
NSKeyedArchiver 把对象写到二进制流中去

如下,在ZYRestaurant定义了如下这些属性


@interface ZYRestaurant : NSObject<NSCoding>

@property (nonatomic,copy) NSString *shopID;
@property (nonatomic,copy) NSString *title;
@property (nonatomic,copy) NSString *discount;
@property (nonatomic,copy) NSString *address;

@end

在ZYRestaurant.m里面实现如下这些方法

- (id)initWithCoder:(NSCoder *)aDecoder
{
    if (self = [super init]) {
        _shopID = [aDecoder decodeObjectForKey:@"shopid"];
        _title = [aDecoder decodeObjectForKey:@"title"];
        _discount = [aDecoder decodeObjectForKey:@"discount"];
        _address = [aDecoder decodeObjectForKey:@"address"];
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder
{
    [aCoder encodeObject:self.shopID forKey:@"shopid"];
    [aCoder encodeObject:self.title forKey:@"title"];
    [aCoder encodeObject:self.discount forKey:@"discount"];
    [aCoder encodeObject:self.address forKey:@"address"];
}
]]>
loadView与viewDidLoad的区别 2014-06-26T01:30:33+00:00 lazyyang /2014/06/26/loadviewyu-viewdidloadde-qu-bie 观察下面这个例子会出现什么结果

1 -(void)loadView
2 {
3   NSLog(@"who am i");
4 }
5 
6 -(void)viewDidLoad
7 {
8     self.view.backgroundColor = [UIColor redColor];
9 }

分析:发现loadView和viewDidload会一直循环调用,原因何在

我们发现只要self.view为nil,它就会调用一次loadview

于是我们猜想_view的getter方法里是这样写的

if(_view == nil)
{
...
   [self loadView];
   [self viewDidLoad];
...
}

下面是一涨生命周期的图:

]]>
腾讯iOS面试题 2014-06-25T01:26:26+00:00 lazyyang /2014/06/25/teng-xun-iosmian-shi-ti 腾讯

昨天去面腾讯地图的iOS,下面是我总结的一些问题。

  1. 假如沙盒路径下有一个文件,文件里面有1000万个学生成绩,怎么对这个学生的成绩进行排序让其效率更高(假设内存足够大,可以一次性载入1000万个学生)

  2. 从1000万个数里以最快的效率选出其中最大的100个(假设内存足够大,可以一次性载入1000万个成绩)

  1. 一个简单的游戏
    a.假如有3个杯子,其中有一个杯子的下面有钻石,现在让你选一个, 然后系统帮你去除一个没有钻石的杯子,现在问你要不要换另外一个杯子(也就是问剩下两个杯子下面有钻石的概率是不是一样的)
    b.换一种情况。假如有1000万张彩票(只有一张中奖),先让你从1000玩里随便选一个,然后系统帮你去除其中9999998张没中奖的,留下你选的一张和另外一张,问你要不要换成剩下的另一张(同样是问剩下两张彩票的中奖概率是不是一样的)

  2. 你对iOS哪一块最了解?然后针对那一点各种问。我说我对下载模块比较熟悉,然后他就各种问。例如通过NSURLCOnnection下载的数据与UI交互的优化、断点下载的实现原理等

  3. LRUCache算法.比如你有10M的内存,怎么去处理20M的文件

  4. 你对MVC的理解,你如何你构架一个项目

  5. 短信功能中,scrollview下移量和手势下移量不成比例,求这两个下移量的关系算法

总结

其实腾讯的面试,更多是引导型的面试。即使你iOS全部会,你说你的算法很厉害(那面试官可能更侧重的去问你算法),只要你有一点能够打动面试官,那么得到offer的可能性也就更大了。

]]>
在Octopress上搭建一个github博客 2014-05-23T16:06:41+00:00 lazyyang /2014/05/23/create-octopress-blog 安装
  1. 首先我们得先配置ruby、git环境,这里就不讲了。
  2. 先cd到一个好的目录,比如Desktop,然后开始下载Octopress
    $ git clone https://github.com/imathis/octopress.git
    $ cd octopress
  3. 安装一些东西
    $ gem install bundler
    $ rbenv rehash
    $ bundle install

这样就装好Octopress了,我们可以rake preview在本地查看,如果rake preview报错的话,如下,

You have already activated rake 10.1.1, but your Gemfile requires rake 0.9.2.2. Prepending bundle exec to your command may solve this.

可能是因为你的rake版本过高,这时候就需要你在rake preview的前面加上bundle exec

发布


  1. 在github上创建一个名为username.github.com的repository
  2. 在Octopress目录里设定资料
    $ rake setup_github_pages
  3. 生成HTML
    $ rake generate
  4. 发布
    $rake deploy

这样等几分钟,你就可以在你的网页,例如http://lazyyang.github.com上浏览你的博客了

配置博客


我们需要在github上另外创建一个source分支,这个分支专门用来博客相关属性、写博客以及主题等

配置博客相关属性

  • 配置博客,相关配置文件在_config.yml文件里 $ vi _config.yml
    其中url是必填项,为你的博客地址

写博客

  • 写博客
    $ cd source
    $ cd _posts
    $ rake new_post["博客名字"]
    这样就创建了一个名为markdown的文件,然后在markdown里面写博客了
  • 同步github
    $ git add .
    $ git commit -m "a new blog"
    $ git pull orgin source
    $ git push origin source
  • 发布文章
    $ rake generate
    $ rake deploy
    这样文章就成功同步到blog上去了

:##多台电脑管理博客


目前github上已经部署了我们的博客了,现在需要在另外一台电脑上继续写博客,该怎么处理
Octopress的git仓库(repository)有两个分支,分别是mastersourcemaster存储的事博客网站本身,而source存储的是生成博客的源文件。master的内容放在根目录的_deploy

  • 将source分支clone到本地
    $ git clone -b source git@github.com:username/username.github.com.git octopress

  • 然后将master的clone到octopress的_deploy文件夹内
    $ cd octopress
    $ git clone git@github.com:username/username.github.com.git _deploy

  • 然后安装博客,即bundler和需要的rubygem
    $ gem install bundler
    $ rbenv rehash
    $ bundle install
    $ rake setup_github_pages

]]>