Gowhich

Durban's Blog

IOS开发会遇到很多的问题,但是如果有一些好的插件的话,应该可以解决很多的问题的,尤其是在项目中,有了一些简便的插件,能够解决很大的问题。

全能搜索家CodePilot 2.0

你要找的是文件?是文件夹?是代码?Never Mind,CMD+SHIFT+X调出CodePilot,输入任何你想到搜的东西吧!想搜 appFinishLaunchingWithOptions?忘记咋拼了?没关系强大的代码搜索能力,appflaun一样也可以找到!超级强大的正则 匹配,匹配任何你所想!

项目地址:http://codepilot.cc

Vim控必备的XVim

XVim是一个针对Xcode的Vim插件,能让开发者在不放弃任何xcode功能的前提下体验vim的功能。

项目地址:https://github.com/JugglerShu/XVim

YouCompleteMe(vim的插件)

如果你比较喜欢用vim来写代码的话,这里有一个非常棒的vim插件——YouCompleteMe——当你在编写OC代码时,可以提升体验。 YouCompleteMe可以在Vim中添加代码自动补全功能,并且不需要你来按某个键来查看代码补全建议——针对OC、OC++、C++以及C该插件 可以自动补全建议。

项目地址:https://github.com/Valloric/YouCompleteMe

XCode颜色显示插件ColorSense

代码里的那些冷冰冰的颜色数值,到底时什么颜色?如果你经常遇到这个问题,每每不得不运行下模拟器去看看,那么这个插件绝对不容错过。更彪悍的是你甚至可以点击显示的颜色面板,直接通过系统的ColorPicker来自动生成对应颜色代码,再也不用做各种颜色代码转换了!

项目地址: https://github.com/omz/ColorSense-for-Xcode

大段文本利器HOStringSense

经常输入大段文本的时候,如果文本里面有各种换行和特殊字符,经常会让人很头疼,有了HOStringSense,再也不不用为这个问题犯愁了,顺便附送字数统计功能。

项目地址:https://github.com/holtwick/HOStringSense-for-Xcode

规范注释生成器VVDocumenter

很多时候,为了快速开发,很多的技术文档都是能省则省,这个时候注释就变得异常重要,再配合Doxygen这种注释自动生成文档的,就完美了。但 是每次都要手动输入规范化的注释,着实也麻烦,但有了VVDocumenter,规范化的注释,主需要输入三个斜线“///”,就OK啦! (VVDocumenter在Mac OSX 10.8.5和Xcode 4.6.3上进行开发,应该能支持所有Xcode 4版本,如果想支持Xcode 5,可以对plist文件稍作修改。

项目地址:https://github.com/onevcat/VVDocumenter-Xcode

CocoaPods for Xcode

非常方便的Xcode pods插件。可以很方便的在Xcode通过pods安装各种objective-c第三方库,省去以前还要手动去跑pods命令行的麻烦;此外,还支持 通过cocoaDocs来安装库文档。唯一的遗憾是,它目前只支持Xcode5,4版本还用不了。

项目地址:https://github.com/kattrali/cocoapods-xcode-plugin

Xcode语法高亮插件

以前用eclipse开发,自带的有语法高亮的效果。做ios开发也许久了,但是没发现一款语法高亮的插件,因为xcode自己的效果是仅在变量 或类名下面加了个虚线,平时看起代码来十分不舒服,最近果断为xcode写了一款语法高亮的插件,不过功能非常有限,没有eclipse的那么好用,也没 对对象的作用域区分,勉强能使用吧。和有需要的分享一下吧。

下载附件,解压后放在:你的用户/Library/Application Support/Developer/Shared/Xcode/Plug-ins目录下,有的童鞋还没有Plug-ins这个目录吧,那就手动建一个, 然后把解压后的highlight-Plugin.xcplugin放进去,重启xcode即可。然后就能看到高亮的菜单了。

项目地址: http://www.cocoachina.com/bbs/read.php?tid=150107

KSImageNamed-Xcode

为项目中使用的UIImage的imageNamed提供文件名自动补全功能。使用[UIImage imageNamed:@”xxx”]时,该插件会扫描整个workspace中的图片文件。

项目地址: https://github.com/ksuther/KSImageNamed-Xcode

xcode-extend-plug-in

帮助你快速格式化代码、生成注释、复制一行等。

项目地址: https://code.google.com/p/xcode-extend-plug-in/

XcodeColors

改变调试控制台颜色

项目地址: https://github.com/robbiehanson/XcodeColors

SCXcodeMiniMap

一个Xcode插件,可以在当前的窗口内创建一个代码迷你地图,并在屏幕上高亮提示。

项目地址: https://github.com/stefanceriu/SCXcodeMiniMap

Lin本地化字符串

之前我们提到过一个开源的Mac基础工具SCStringsUtility,可以让你在一个清爽的界面编辑不同的语言,简单地输入/输出 NSLocalizedString数据。Lin是一款功能相近的Xcode插件,提供了一个非常不错的操作界面,并且为不同的语言提供了不同的区域。

项目地址:https://github.com/questbeat/Lin

插件管理Alcatraz

Alcatraz是一个开源的Xcode 4包管理器,可以让你更便捷地发现、安装以及管理插件、模板和配色方案。只需要简单地点击或者勾选,不需要手工复制和粘贴。

项目地址: https://github.com/mneorr/Alcatraz

Bezels是Mac OS X下,苹果私有的UI控件,用来提示用户某些信息。不过iOS下,就有很多第三方的Bezels风格的提示控件实现了。相信大家也见过很多了。下图左侧是OSX的提示音量变化的Bezels,右侧是iOS上的Bezels风格的进度提示。

在iOS上做Bezels的思路很简单,无非就是在UIView里绘制一个半透明的圆角矩形,然后加入其他的SubView。因为没有现成的绘制圆角矩形的API可用,所以我们来自己绘制一个——反正是为了学习嘛。

新建项目,添加一个UIView的子类,修改drawRect方法,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
- (void)drawRect:(CGRect)rect
{
CGFloat width = rect.size.width;
CGFloat height = rect.size.height;
// 简便起见,这里把圆角半径设置为长和宽平均值的1/10
CGFloat radius = (width + height) * 0.05;

// 获取CGContext,注意UIKit里用的是一个专门的函数
CGContextRef context = UIGraphicsGetCurrentContext();
// 移动到初始点
CGContextMoveToPoint(context, radius, 0);

// 绘制第1条线和第1个1/4圆弧
CGContextAddLineToPoint(context, width - radius, 0);
CGContextAddArc(context, width - radius, radius, radius, -0.5 * M_PI, 0.0, 0);

// 绘制第2条线和第2个1/4圆弧
CGContextAddLineToPoint(context, width, height - radius);
CGContextAddArc(context, width - radius, height - radius, radius, 0.0, 0.5 * M_PI, 0);

// 绘制第3条线和第3个1/4圆弧
CGContextAddLineToPoint(context, radius, height);
CGContextAddArc(context, radius, height - radius, radius, 0.5 * M_PI, M_PI, 0);

// 绘制第4条线和第4个1/4圆弧
CGContextAddLineToPoint(context, 0, radius);
CGContextAddArc(context, radius, radius, radius, M_PI, 1.5 * M_PI, 0);

// 闭合路径
CGContextClosePath(context);
// 填充半透明黑色
CGContextSetRGBFillColor(context, 0.0, 0.0, 0.0, 0.5);
CGContextDrawPath(context, kCGPathFill);
}

其实这用到的只是些很基本的Quartz绘图函数而已。不过,我们顺利的绘制出了半透明,黑色的,圆角Bezel风格的View

在CentOS下配置iptables防火墙,是非常必要的。来我们学习如何配置!

在Linux中设置防火墙,以CentOS为例,打开iptables的配置文件:

1
vi /etc/sysconfig/iptables

通过/etc/init.d/iptables status命令查询是否有打开80端口,如果没有可通过两种方式处理:

1.修改vi /etc/sysconfig/iptables命令添加使防火墙开放80端口

1
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT

2.关闭/开启/重启防火墙

1
/etc/init.d/iptables stop   #start 开启   #restart 重启

3.永久性关闭防火墙

1
chkconfig --level 35 iptables off   /etc/init.d/iptables stop   iptables -P INPUT DROP

4.打开主动模式21端口

1
iptables -A INPUT -p tcp --dport 21 -j ACCEPT

5.打开被动模式49152~65534之间的端口

1
2
3
iptables -A INPUT -p tcp --dport 49152:65534 -j ACCEPT   
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m state --state ESTABLISHED -j ACCEPT

注意:

一定要给自己留好后路,留VNC一个管理端口和SSh的管理端口

需要注意的是,你必须根据自己服务器的情况来修改这个文件。

全部修改完之后重启iptables:

1
service iptables restart

你可以验证一下是否规则都已经生效:

1
iptables -L -a

恶意解析 是指有人通过域名A记录直接解析别人IP地址,从而得到一个在访问者眼中完全相同网站,也会造成搜索引擎收录别人的域名
主要被称作:PR劫持,恶意网站镜像,恶意克隆,恶意解析域名到自己的服务器等

给lnmp环境设置空头主机是很有必要的,比如说lnmp环境默认的虚拟空间目录是”/home/wwwroot”当然不建议大家把站点绑定在这个目录里,因为处于安全考虑最好是自己新建一个目录!lnmp环境安装完后”phpmyadmin”和”FTP”默认是装在”/home/wwwroot” 的目录里,比如访问”phpmyadmin”那么路径应该是:”IP/phpmyadmin/” . 但有些无聊的人把自己的域名解析到你的IP上.这样通过他的域名可以直接访问你的站点,这点让人很不爽.所以,给lnmp设置空头主机是非常必要的,除非你为FTP和phpmyadmin分别绑定独立的域名,但这样不方便管理..

以下是空主机头设置方法:

第一步: 使用 /root/vhost.sh 建立虚拟主机的命令来绑定你一开始设置的那个二级域名到 /home/wwwroot(wwwroot后面没有”/”)

第二步: 通过SSH客户端,找到 usr/local/nginx/conf/nginx.conf (请看好这个目录,)把里面的

server段中的

1
2
3
4
5
6
server {
listen 80;
server_name www.xxx.com;
index index.html index.htm index.php;
root /home/wwwroot;
}

改成

1
2
3
4
server {
listen 80 default;
return 500;
}

然后保存。

第三步: 重新启动Nginx服务器,命令是 /usr/local/nginx/sbin/nginx -s reload

自己想管理”/home/wwwroot ”目录内容只需要输入”域名/目录/”便可以了!别人解析到你的IP返回的是500错误

php有两个扩展可以实现web service,一个是NuSoap,一个是php 官方的soap扩展,由于soap是官方的,所以我们这里以soap来实现web service.由于默认是没有打开soap扩展的,所以自己先看一下soap扩展有没有打开。

在soap编写web service的过程中主要用到了SoapClient,SoapServer,SoapFault三个类。

SoapClient类

这个类用来使用Web services。SoapClient类可以作为给定Web services的客户端。
它有两种操作形式:

  • * WSDL 模式

  • * Non-WSDL 模式

在WSDL模式中,构造器可以使用WSDL文件名作为参数,并从WSDL中提取服务所使用的信息。

non-WSDL模式中使用参数来传递要使用的信息。

SoapServer类

这个类可以用来提供Web services。与SoapClient类似,SoapServer也有两种操作模式:WSDL模式和non-WSDL模式。这两种模式的意义跟 SoapClient的两种模式一样。在WSDL模式中,服务实现了WSDL提供的接口;在non-WSDL模式中,参数被用来管理服务的行为。

在SoapServer类的众多方法中,有三个方法比较重要。它们是SoapServer::setClass()SoapServer::addFunction()SoapServer::handle()

下面给出实例:
定义一个提供服务的php类,这个类所提供的函数就是web service对外提供的服务

1
2
3
4
5
6
7
<?php
class PersonInfo{
public function getName(){
return "My name is David";
}
}
?>

服务器端的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
require_once 'PersonInfo.php';
//wsdl方式提供web service,如果生成了wsdl文件则可直接传递到//SoapServer的构造函数中
//$s = new SoapServer('PersonInfo.wsdl');

//doesn't work 只有location不能提供web service
//output:looks like we got no XML document
//$s = new SoapServer(null,array("location"=>"http://localhost/Test/MyService/Server.php"));

//下面两种方式均可以工作,只要指定了相应的uri
//$s = new SoapServer(null,array("uri"=>"Server.php"));
$s = new SoapServer(null,array("location"=>"http://local.ubuntu.test.com/soapserver/server.php","uri"=>"server.php"));
$s->setClass("PersonInfo");
$s->handle();
?>

客户端代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
try{
//wsdl方式调用web service
//wsdl方式中由于wsdl文件写定了,如果发生添加删除函数等操作改动,不会反应到wsdl,相对non-wsdl方式
//来说不够灵活
//$soap = new SoapClient("http://localhost/Test/MyService/PersonInfo.wsdl");

//non-wsdl方式调用web service
//在non-wsdl方式中option location是必须提供的,而服务端的location是选择性的,可以不提供
$soap = new SoapClient(null,array('location'=>"http://local.ubuntu.test.com/soapserver/server.php",'uri'=>'server.php'));

//两种调用方式,直接调用方法,和用__soapCall简接调用
$result1 = $soap->getName();
$result2 = $soap->__soapCall("getName",array());
echo $result1."<br/>";
echo $result2;

}catch(SoapFault $e){
echo $e->getMessage();
}catch(Exception $e){
echo $e->getMessage();
}
?>

输出的结果为:

1
2
My name is David
My name is David

如果数据库里面的数据有问题了,或者是有人捣乱,再或者就是您老人家看这条数据不爽,还有就是您想毁灭证据(其实总是会留下痕迹的)的时候,你就需要了解MySQL的Delete语句了。MySQL为我们提供了delete和truncate语句来删除数据。

delete 语句的定义:

经常和数据库打交道的孩子们,删除数据的时候用的大多都是 delete 语句。现在让我们来看一下 delete语句的定义。

1
2
3
4
DELETE [LOW_PRIORITY] [QUICK] [IGNORE] FROM tbl_name
[WHERE where_definition]
[ORDER BY ...]
[LIMIT row_count]

delete 语句的示例:

MySQL的这些语法都和口语类似,你要指出你想从哪个表删除数据,还有删除哪些数据,这就够了。就像写记叙文的时候,时间、地点、人物、环境、情节几要素必不可少一样。

示例是最形象,最能说明问题的。按照上面的语法结构,我想删除 firends 表中所有 user_name 等于 simaopig 的记录,就可以使用如下SQL语句:

1
delete from friends where user_name = 'simaopig';

delete 注意事项:

从语法结构中,我们就可以看出,和 update 语法一样,我们是可以省略 where 子句的。不过这是一个很危险的行为。因为如果不指定 where 子句,delete 将删除表中所有的记录,而且是立即删除,即使你想哭都没有地方,也没有时间,因为你需要马上和主管承认错误,并且立即找出MySQL日志,来回滚记录。不过一旦你有过一次这样的经历,我相信这一定是印象深刻的。

truncate 语句的简单说明:

这个语句之前我也没有接触过,也没有使用过。因为一般情况下,删除数据大家都在使用delete语句。其实这个 truncate 命令很简单,它的意思是:删除表的所有记录。相当于 delete 语句不写 where 子句一样。其语法结构为:

1
TRUNCATE [TABLE] tbl_name

这里简单的给出个示例,我想删除 friends 表中所有的记录,可以使用如下语句:

1
truncate table friends;

truncate 和 delete的效率问题:

如果想要删除表的所有数据,truncate语句要比 delete 语句快。因为 truncate 删除了表,然后根据表结构重新建立它,而 delete 删除的是记录,并没有尝试去修改表。这也是为什么当向一个使用 delete 清空的表插入数据时,MySQL 会记住前面产生的AUTOINCREMENT序列,并且继续利用它对AUTOINCREMENT字段编号。而truncate删除表后,表是从1开始为autoincrement字段编号。

不过truncate命令快规快,却不像delete命令那样对事务处理是安全的。因此,如果我们想要执行truncate删除的表正在进行事务处理,这个命令就会产生退出并产生错误信息。

Redis提供了丰富的命令(command)对数据库和各种数据类型进行操作,这些command可以在Linux终端使用。在编程时,比如使用Redis 的Java语言包,这些命令都有对应的方法。下面将Redis提供的命令做一总结。

官网命令列表:http://redis.io/commands (英文)

连接操作相关的命令

quit:关闭连接(connection)

auth:简单密码认证

对value操作的命令

exists(key):确认一个key是否存在

del(key):删除一个key

type(key):返回值的类型

keys(pattern):返回满足给定pattern的所有key

randomkey:随机返回key空间的一个key

rename(oldname, newname):将key由oldname重命名为newname,若newname存在则删除newname表示的key

dbsize:返回当前数据库中key的数目

expire:设定一个key的活动时间(s)

ttl:获得一个key的活动时间

select(index):按索引查询

move(key, dbindex):将当前数据库中的key转移到有dbindex索引的数据库

flushdb:删除当前选择数据库中的所有key

flushall:删除所有数据库中的所有key

对String操作的命令

set(key, value):给数据库中名称为key的string赋予值value

get(key):返回数据库中名称为key的string的value

getset(key, value):给名称为key的string赋予上一次的value

mget(key1, key2,…, key N):返回库中多个string(它们的名称为key1,key2…)的value

setnx(key, value):如果不存在名称为key的string,则向库中添加string,名称为key,值为value

setex(key, time, value):向库中添加string(名称为key,值为value)同时,设定过期时间time

mset(key1, value1, key2, value2,…key N, value N):同时给多个string赋值,名称为key i的string赋值value i

msetnx(key1, value1, key2, value2,…key N, value N):如果所有名称为key i的string都不存在,则向库中添加string,名称key i赋值为value i

incr(key):名称为key的string增1操作

incrby(key, integer):名称为key的string增加integer

decr(key):名称为key的string减1操作

decrby(key, integer):名称为key的string减少integer

append(key, value):名称为key的string的值附加value

substr(key, start, end):返回名称为key的string的value的子串

对List操作的命令

rpush(key, value):在名称为key的list尾添加一个值为value的元素

lpush(key, value):在名称为key的list头添加一个值为value的 元素

llen(key):返回名称为key的list的长度

lrange(key, start, end):返回名称为key的list中start至end之间的元素(下标从0开始,下同)

ltrim(key, start, end):截取名称为key的list,保留start至end之间的元素

lindex(key, index):返回名称为key的list中index位置的元素

lset(key, index, value):给名称为key的list中index位置的元素赋值为value

lrem(key, count, value):删除count个名称为key的list中值为value的元素。count为0,删除所有值为value的元素,count>0从 头至尾删除count个值为value的元素,count<0从尾到头删除|count|个值为value的元素。 lpop(key):返回并删除名称为key的list中的首元素 rpop(key):返回并删除名称为key的list中的尾元素 blpop(key1, key2,… key N, timeout):lpop命令的block版本。即当timeout为0时,若遇到名称为key i的list不存在或该list为空,则命令结束。如果timeout>0,则遇到上述情况时,等待timeout秒,如果问题没有解决,则对 keyi+1开始的list执行pop操作。

brpop(key1, key2,… key N, timeout):rpop的block版本。参考上一命令。

rpoplpush(srckey, dstkey):返回并删除名称为srckey的list的尾元素,并将该元素添加到名称为dstkey的list的头部

对Set操作的命令

sadd(key, member):向名称为key的set中添加元素member

srem(key, member) :删除名称为key的set中的元素member

spop(key) :随机返回并删除名称为key的set中一个元素

smove(srckey, dstkey, member) :将member元素从名称为srckey的集合移到名称为dstkey的集合

scard(key) :返回名称为key的set的基数

sismember(key, member) :测试member是否是名称为key的set的元素

sinter(key1, key2,…key N) :求交集

sinterstore(dstkey, key1, key2,…key N) :求交集并将交集保存到dstkey的集合

sunion(key1, key2,…key N) :求并集

sunionstore(dstkey, key1, key2,…key N) :求并集并将并集保存到dstkey的集合

sdiff(key1, key2,…key N) :求差集

sdiffstore(dstkey, key1, key2,…key N) :求差集并将差集保存到dstkey的集合

smembers(key) :返回名称为key的set的所有元素

srandmember(key) :随机返回名称为key的set的一个元素

对zset(sorted set)操作的命令

zadd(key, score, member):向名称为key的zset中添加元素member,score用于排序。如果该元素已经存在,则根据score更新该元素的顺序。

zrem(key, member) :删除名称为key的zset中的元素member

zincrby(key, increment, member) :如果在名称为key的zset中已经存在元素member,则该元素的score增加increment;否则向集合中添加该元素,其score的值为increment

zrank(key, member) :返回名称为key的zset(元素已按score从小到大排序)中member元素的rank(即index,从0开始),若没有member元素,返回“nil”

zrevrank(key, member) :返回名称为key的zset(元素已按score从大到小排序)中member元素的rank(即index,从0开始),若没有member元素,返回“nil”

zrange(key, start, end):返回名称为key的zset(元素已按score从小到大排序)中的index从start到end的所有元素

zrevrange(key, start, end):返回名称为key的zset(元素已按score从大到小排序)中的index从start到end的所有元素

zrangebyscore(key, min, max):返回名称为key的zset中score >= min且score <= max的所有元素 zcard(key):返回名称为key的zset的基数 zscore(key, element):返回名称为key的zset中元素element的score zremrangebyrank(key, min, max):删除名称为key的zset中rank >= min且rank <= max的所有元素 zremrangebyscore(key, min, max) :删除名称为key的zset中score >= min且score <= max的所有元素

zunionstore / zinterstore(dstkeyN, key1,…,keyN, WEIGHTS w1,…wN, AGGREGATE SUM|MIN|MAX):对N个zset求并集和交集,并将最后的集合保存在dstkeyN中。对于集合中每一个元素的score,在进行 AGGREGATE运算前,都要乘以对于的WEIGHT参数。如果没有提供WEIGHT,默认为1。默认的AGGREGATE是SUM,即结果集合中元素 的score是所有集合对应元素进行SUM运算的值,而MIN和MAX是指,结果集合中元素的score是所有集合对应元素中最小值和最大值。

对Hash操作的命令

hset(key, field, value):向名称为key的hash中添加元素field<—>value

hget(key, field):返回名称为key的hash中field对应的value

hmget(key, field1, …,field N):返回名称为key的hash中field i对应的value

hmset(key, field1, value1,…,field N, value N):向名称为key的hash中添加元素field i<—>value i

hincrby(key, field, integer):将名称为key的hash中field的value增加integer

hexists(key, field):名称为key的hash中是否存在键为field的域

hdel(key, field):删除名称为key的hash中键为field的域

hlen(key):返回名称为key的hash中元素个数

hkeys(key):返回名称为key的hash中所有键

hvals(key):返回名称为key的hash中所有键对应的value

hgetall(key):返回名称为key的hash中所有的键(field)及其对应的value

持久化

save:将数据同步保存到磁盘

bgsave:将数据异步保存到磁盘

lastsave:返回上次成功将数据保存到磁盘的Unix时戳

shundown:将数据同步保存到磁盘,然后关闭服务

远程服务控制

info:提供服务器的信息和统计

monitor:实时转储收到的请求

slaveof:改变复制策略设置

config:在运行时配置Redis服务器

设备方向改变通知

iOS设备内置有加速计(Accelerometer)可以检测出当前设备的方法,当设备方向改变时,系统会发送UIDeviceOrientationDidChangeNotification通知,UIKit框架会监听此通知并根据当前应用设置来自动更新用户界面方向(Interface Orientation)。

在iOS6版本中,设置应用支持的界面方向的方法和iOS5及之前的版本有差异,下面将分别介绍两个版本下的界面方向设置。

iOS6中设置支持的界面方向(Interface Orientations)

在iOS6中,UIKit框架接收到方向改变的通知时,会使用UIApplication的全局设置和当前topmost view controller的设置来共同判断是否允许旋转至此方向。

1,Info.plist中的UISupportedInterfaceOrientations

设置Info.plist中的UISupportedInterfaceOrientations对应的数组可控制app全局支持的方向,此设置对app的所有view controller都有效。使用当前topmost view controller支持的方向与此设置的与(AND)来确定给定的方向是否支持。与操作的结果不能为0,若为0,则会引发UIApplicationInvalidInterfaceOrientationException异常。

2,Topmost view controller支持的方向

  1. Topmost view controller是什么?

    Important: Here, the topmost view controller refers to the window’s root view controller unless another view controller is currently presented, in which case, the presented view controller becomes the topmost view controller for the duration it is presented. This should not be confused with the topViewController of a navigation controller.

    如Technical Q&A QA1688中所述,topmost view controller有两类:

    a. window的root view controller(当没有其他view controller正在被presented时);

    b. 当前正在被presented显示的其他view controller。

    查找当前topmost view controller的代码为:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    +(UIViewController*) topMostController
    {
    UIViewController*topController =[UIApplication sharedApplication].keyWindow.rootViewController;

    while(topController.presentedViewController){
    topController = topController.presentedViewController;
    }

    return topController;
    }

    注(重要):要使iOS6中的方向旋转相关的方法生效,必须设置window的rootViewController属性,而不能简单的将root view controller的view加入到window中。

  2. UIViewController的方向设置(topmost view controller)

    a.supportedInterfaceOrientations 设置view controller支持的界面方向(Interface Orientations)

    可以通过重写UIViewController的supportedInterfaceOrientations方法来确定当前view controller支持的Interface Orientation。默认情况下,iPad支持所有四个方向,iPhone支持除UIInterfaceOrientationPortraitUpsideDown以外的三个方向。下面为设置支持portrait方法和 landscape-left方向的代码。

    1
    2
    3
    4
    - (NSUInteger)supportedInterfaceOrientations
    {
    return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft;
    }

    b. shouldAutorotate 设置是否允许自动旋转

    如果想暂时禁掉自动旋转,但不想修改supportedInterfaceOrientations中的方向mask,可重写shouldAutorotate方法,如果shouldAutorotate返回NO,则不进行旋转。

    c. preferredInterfaceOrientationForPresentation 设置View Controller被Presented时的首选显示方向。

    当view controller被presented显示时,可能在一个特定的方向显示最合适,如果其仅仅支持这一个方向,可以在supportedInterfaceOrientations方法中简单的返回此方向。但如果view controller支持多个方向显示,但在某一个方向显示最佳,则可以通过重写preferredInterfaceOrientationForPresentation方法来返回此方向。这样,当view controller被presented时,将会以preferredInterfaceOrientationForPresentation返回的方向显示。<\br>
    注意:preferredInterfaceOrientationForPresentation返回的方向是supportedInterfaceOrientations中的一个。

iOS5及之前版本中设置支持的界面方向(Interface Orientations)

1.Info.plist中的UISupportedInterfaceOrientations

与iOS6版本不同,iOS5版本中的UISupportedInterfaceOrientations对应的方向数组仅仅作为提示,不参与能否旋转至特定方向的判断

2.shouldAutorotateToInterfaceOrientation:

默认情况下,此方法只对UIInterfaceOrientationPortrait返回YES,如果view controller需要支持其他方向,则需要重写此方法,对支持的方向返回YES。

当对容器类view controller(container view controller),比如UINavigationController,UITabBarController调用shouldAutorotateToInterfaceOrientation:方法时,其会对所有的子view controller调用同样的方法,如果子view controller不支持参数中指定的方法,同样返回NO。所以,在iOS5中,如果topmost view controller是个container view controller,则其子view controllers同样会参与旋转方向的判断。

为了尽量遵循和iOS6一致的策略,应该在同时支持iOS5中的应用中做如下设置

1,对topmost view controller,仅返回其所支持的界面方向

2,在子view controller中,支持所有的界面方向,并合理设置自适应的view layout来支持不同的页面分辨率

界面方向旋转过程

当旋转发生时,在旋转的各个阶段对会调用topmost view controller中合适的方法,若topmost view controller是个container view controller,则会继续调用当前可见的子view controller中的对应方法。view contoller可以在这些方法中隐藏或者显示视图,调整视图的位置或者大小,或者将方向变化通知给app的其它部分。因为这些方法在旋转的过程中调用,所有不能做任何耗时操作。应该避免在这些方法中替换整个view 架构。

相关方法的调用顺序为:

  1. window调用view controller的willRotateToInterfaceOrientation:duration:方法

    在此方法中,所有调整都还未进行。view的bounds,view controller的interfaceOrientation以及UIApplication的statusBarOrientation都为原来的值;可在此方法中隐藏视图或者做其他view布局的改变。

  2. window改变view的bounds。改变view的bounds会引发下面一系列操作(假定未使用autolayout):

    注:viewWillLayoutSubviews,viewDidLayoutSubviews方法在iOS5.0引入。

        a. view及其子views根据自身的autoresizing掩码设置重新调整大小。
    
        b. 调用view controller的viewWillLayoutSubviews方法。  
         在此方法运行时,view的bounds、view controller的interfaceOrientation以及UIApplication的statusBarOrientation都已经更新为新值。
    
        c. 调用view的layoutsubviews方法。
    
        d. 调用view controlelr的viewDidLayoutSubviews方法。
    
  3. 调用view controller的willAnimateRotationToInterfaceOrientation:duration:方法。

    此方法在动画块(animation block)中被调用,所以在此方法中的属性改变也参与到旋转动画中。

  4. 旋转动画终止。

  5. 调用view controller的didRotateFromInterfaceOrientation: 方法通知旋转动画已终止。

隐藏view controller的界面旋转

若旋转发生时,view controller的内容没有显示在屏幕上(比如present了一个全屏view controller),则此view controller不会收到上述的旋转消息。当此view controller重新显示时,其view会被调整位置和大小以适应新的界面方法,其过程如上述中的步骤2。

横屏竖屏使用不同的ViewController

如果一个页面在横屏和竖屏数据显示方式差别很大,可以使用两个单独的view controllers,一个使用竖屏显示,另一个使用横屏显示。使用两个view controllers与每次选择都调整整个view架构相比简单而且效率高。

其实现方式如下:

  1. 实现两个view controller对象,一个做竖屏显示布局,另一个做横屏显示布局。按照本文前面所述方法设置每个view controller只支持特定的一种界面方向,以防止在设备旋转时,view controller内部自行做旋转操作。

  2. 主view controller(通常为竖屏的那个)注册

    接收UIDeviceOrientationDidChangeNotification通知,在通知的处理方法中,根据当前的设备方向presen或dismiss另一个view controller。

    参考代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    @implementation PortraitViewController
    {
    BOOL isShowingLandscapeView;
    LandscapeViewController *landscapeViewController;
    }
    - (id)init
    {
    self = [super init];
    if (self) {
    landscapeViewController = [[LandscapeViewController alloc]init];

    [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:nil];
    }
    return self;
    }

    -(void)orientationChanged:(NSNotification *)notification
    {
    UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
    if (UIDeviceOrientationIsLandscape(deviceOrientation) && !isShowingLandscapeView) {
    [self presentModalViewController:landscapeViewController animated:YES];
    isShowingLandscapeView = YES;
    }else if(UIDeviceOrientationIsPortrait(deviceOrientation) && isShowingLandscapeView){
    [self dismissModalViewControllerAnimated:YES];
    isShowingLandscapeView = NO;
    }
    }

    -(NSUInteger)supportedInterfaceOrientations
    {
    return UIInterfaceOrientationMaskPortrait;
    }

    -(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
    {
    if (UIInterfaceOrientationIsPortrait(toInterfaceOrientation)) {
    return YES;
    }
    return NO;
    }

    ...
    @end

    @implementation LandscapeViewController

    - (id)init
    {
    self = [super init];
    if (self) {
    self.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
    }
    return self;
    }

    -(NSUInteger)supportedInterfaceOrientations
    {
    return UIInterfaceOrientationMaskLandscape;
    }

    -(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
    {
    if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation)) {
    return YES;
    }
    return NO;
    }

    ...
    @end

Contrainer View controller与设备旋转

Container View Controller在设备旋转的过程中扮演重要的角色。在iOS5旋转方向判断时,Container View Controller会调用子View controller的shouldAutorotateToInterfaceOrientation:方法,判断是否支持某一方向。在界面旋转的过程中,Container View Controller会调用当前正在显示的子View Controllers的willRotateToInterfaceOrientation:duration:等一系列方法。

对于系统定义的Container View Controller,如UINavigationController,UITabBarViewController,上述过程是自动进行的。对于自定义的Container View Controller,则在添加/删除child view controllers时要做合适的处理,以保证相关消息的正确分发。

  1. 向container View Controller增加child view controller

    1
    2
    3
    4
    5
    6
    7
    - (void) displayContentController: (UIViewController*) content;
    {
    [self addChildViewController:content]; // 1
    content.view.frame = [self frameForContentController]; // 2
    [self.view addSubview:self.currentClientView];
    [content didMoveToParentViewController:self]; // 3
    }

    1.调用container的addChildViewController:方法添加child,调用addChildViewController:会自动调用child的willMoveToParentViewController:方法。

    2.修改child view的frame,并将其加入到container的view架构中。

    3.调用child的 didMoveToParentViewController:方法通知操作已结束。

  2. 从container View Controller移除child view controller

    1
    2
    3
    4
    5
    6
    - (void) hideContentController: (UIViewController*) content
    {
    [content willMoveToParentViewController:nil]; // 1
    [content.view removeFromSuperview]; // 2
    [content removeFromParentViewController]; // 3
    }

    1.调用child的willMoveToParentViewController:,传递参数为nil,告知其将被移除。

    2.将child的view从container的view架构中移出。

    3.调用child的removeFromParentViewController的方法,调用此方法会自动调用child的didMoveToParentViewController:方法。

  3. 在两个child view controllers之间切换,综合上面两个步骤

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    - (void) cycleFromViewController: (UIViewController*) oldC
    toViewController: (UIViewController*) newC
    {
    [oldC willMoveToParentViewController:nil]; // 1
    [self addChildViewController:newC];

    newC.view.frame = [self newViewStartFrame]; // 2
    CGRect endFrame = [self oldViewEndFrame];

    [self transitionFromViewController: oldC toViewController: newC // 3
    duration: 0.25 options:0
    animations:^{
    newC.view.frame = oldC.view.frame; // 4
    oldC.view.frame = endFrame;
    }
    completion:^(BOOL finished) {
    [oldC removeFromParentViewController]; // 5
    [newC didMoveToParentViewController:self];
    }];
    }

    如果不想container自动转发rotation事件给child,则可重写shouldAutomaticallyForwardRotationMethods返回NO,然后在合适的时机手动调用child的willRotateToInterfaceOrientation:duration:等方法。

附加:枚举常量定义

获取当前方向的三个方法

1
2
3
UIViewController的interfaceOrientation;
[[UIApplication sharedApplication] statusBarOrientation];
[[UIDevice currentDevice] orientation];

UIDeviceOrientation定义

1
2
3
4
5
6
7
8
9
typedef enum {
UIDeviceOrientationUnknown,
UIDeviceOrientationPortrait,
UIDeviceOrientationPortraitUpsideDown,
UIDeviceOrientationLandscapeLeft,
UIDeviceOrientationLandscapeRight,
UIDeviceOrientationFaceUp,
UIDeviceOrientationFaceDown
} UIDeviceOrientation;

UIInterfaceOrientation定义

1
2
3
4
5
6
typedef enum {
UIInterfaceOrientationPortrait = UIDeviceOrientationPortrait,
UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
UIInterfaceOrientationLandscapeLeft = UIDeviceOrientationLandscapeRight,
UIInterfaceOrientationLandscapeRight = UIDeviceOrientationLandscapeLeft
} UIInterfaceOrientation;

注意:UIInterfaceOrientationLandscapeLeft定义为UIDeviceOrientationLandscapeRight,UIInterfaceOrientationLandscapeRight定义为UIDeviceOrientationLandscapeLeft,两者是相反的。

UIInterfaceOrientationMask定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
typedef enum {
UIInterfaceOrientationMaskPortrait = (1 << UIInterfaceOrientationPortrait),
UIInterfaceOrientationMaskLandscapeLeft = (1 << UIInterfaceOrientationLandscapeLeft),
UIInterfaceOrientationMaskLandscapeRight = (1 << UIInterfaceOrientationLandscapeRight),
UIInterfaceOrientationMaskPortraitUpsideDown = (1 << UIInterfaceOrientationPortraitUpsideDown),
UIInterfaceOrientationMaskLandscape =
(UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight),
UIInterfaceOrientationMaskAll =
(UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft |
UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskPortraitUpsideDown),
UIInterfaceOrientationMaskAllButUpsideDown =
(UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft |
UIInterfaceOrientationMaskLandscapeRight),
} UIInterfaceOrientationMask;

我们来做一下小的实例,迷你计算器,很简单的,虽然这里初次不是很完善,后面会渐渐完善的。

新建Single View Application项目CaculateUI,类前缀CaculateUI。

CaculateUIAppDelegate两个文件均为自动生成未修改。

新建一个Objective-C Class,名为CalculatorBrain,这个就是MVC模式中的Model,所有的数据处理都在这里,CalculatorViewController取到数据后,作简单的转换以符合Model的要求,再传给Model,由Model处理完返回处理结果给Controller,再由Controller更新View.

CalculatorBrain.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//
// CalculatorBrain.h
// CaculateUI
//
// Created by david on 13-9-10.
// Copyright (c) 2013年 WalkerFree. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface CalculatorBrain : NSObject

//存储执行结果
@property (nonatomic) double result;
//存储操作符
@property (copy, nonatomic) NSString *tempOperateString;


-(void) operate:(double) number:(NSString *) operateString;
-(double) caculate:(double) number;

@end

CalculatorBrain.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
//
// CalculatorBrain.m
// CaculateUI
//
// Created by david on 13-9-10.
// Copyright (c) 2013年 WalkerFree. All rights reserved.
//

#import "CalculatorBrain.h"

@implementation CalculatorBrain

@synthesize result;
@synthesize tempOperateString;

-(void) operate:(double)number :(NSString *)operateString
{
self.result = number;
self.tempOperateString = operateString;
}

-(double) caculate:(double)number
{
//根据操作符执行相关操作
if([self.tempOperateString isEqualToString:@"+"])
{
self.result += number;
}
else if([self.tempOperateString isEqualToString:@"-"])
{
self.result -= number;
}
else if([self.tempOperateString isEqualToString:@"*"])
{
self.result *= number;
}
else if([self.tempOperateString isEqualToString:@"/"])
{
if(number == 0)
{
self.result = 0;
}
else
{
self.result /= number;
}
}

return self.result;
}

@end

由于我这里没有使用xib这种东西,所以下面就感觉比较复杂,不过您如果有更好的方法,请提示我一下,谢谢

CaculateUIViewController.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
//
// CaculateUIViewController.h
// CaculateUI
//
// Created by david on 13-8-29.
// Copyright (c) 2013年 WalkerFree. All rights reserved.
//

#import <UIKit/UIKit.h>
@class CalculatorBrain;
@class FUIButton;

@interface CaculateUIViewController : UIViewController

@property (strong, nonatomic) FUIButton *One;
@property (strong, nonatomic) FUIButton *Two;
@property (strong, nonatomic) FUIButton *Three;
@property (strong, nonatomic) FUIButton *Four;
@property (strong, nonatomic) FUIButton *Five;
@property (strong, nonatomic) FUIButton *Six;
@property (strong, nonatomic) FUIButton *Seven;
@property (strong, nonatomic) FUIButton *Eight;
@property (strong, nonatomic) FUIButton *Nine;
@property (strong, nonatomic) FUIButton *Ten;
@property (strong, nonatomic) FUIButton *Zero;

//小数点
@property (strong, nonatomic) FUIButton *Decimal;
//等号
@property (strong, nonatomic) FUIButton *Equal;
//加号
@property (strong, nonatomic) FUIButton *Plus;
//减号
@property (strong, nonatomic) FUIButton *Minus;
//乘号
@property (strong, nonatomic) FUIButton *Multiply;
//除号
@property (strong, nonatomic) FUIButton *Division;

@property (strong, nonatomic) UILabel *display;
@property (nonatomic) BOOL isEntering;
@property (strong, nonatomic) CalculatorBrain *brain;


//数字键按下操作
-(void) digitalPressed:(id)sender;
//操作符按下操作
-(void) operatePressed:(id)sender;
//等于号按下操作
-(void) enterPressed:(id)sender;




@end

CaculateUIViewController.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
//
// CaculateUIViewController.m
// CaculateUI
//
// Created by david on 13-8-29.
// Copyright (c) 2013年 WalkerFree. All rights reserved.
//

#import "CaculateUIViewController.h"
#import "CalculatorBrain.h"
#import "FUIButton.h"
#import "UIColor+FlatUI.h"
#import "UIFont+FlatUI.h"
#import "UINavigationBar+FlatUI.h"

@interface CaculateUIViewController ()

@end

@implementation CaculateUIViewController

@synthesize One;
@synthesize Two;
@synthesize Three;
@synthesize Four;
@synthesize Five;
@synthesize Six;
@synthesize Seven;
@synthesize Eight;
@synthesize Nine;
@synthesize Ten;
@synthesize Zero;
@synthesize Decimal;
@synthesize Equal;
@synthesize Plus;
@synthesize Minus;
@synthesize Multiply;
@synthesize Division;


@synthesize display;
@synthesize brain;
@synthesize isEntering;

- (void)viewDidLoad
{
// CGFloat btnWidth = 70.0;
CGFloat btnXInterval = 5.0;
CGFloat btnWidth = (self.view.frame.size.width - 2 * 5.0 - 3 * btnXInterval) / 4;


self.title = @"迷你计算器";

[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
display = [[UILabel alloc] initWithFrame:CGRectMake(5.0,
5.0,
310.0,
90.0)];
display.backgroundColor = [UIColor blackColor];
display.text = @"";
display.textAlignment = NSTextAlignmentRight;
display.font = [UIFont systemFontOfSize:40.0];
display.textColor = [UIColor whiteColor];
[self.view addSubview:display];

if(!self.brain)
{
self.brain = [[CalculatorBrain alloc] init];
}


//第一个按钮
One = [FUIButton buttonWithType:UIButtonTypeCustom];
[One setFrame:CGRectMake(5.0,
100.0,
btnWidth,
30.0)];
[One setTitle:@"1" forState:UIControlStateNormal];
[One addTarget:self
action:@selector(digitalPressed:)
forControlEvents:UIControlEventTouchUpInside];
[self decorationButton:One];
[self.view addSubview:One];

//第二个按钮
Two = [FUIButton buttonWithType:UIButtonTypeCustom];
[Two setFrame:CGRectMake(btnWidth + 5.0 + btnXInterval,
100.0,
btnWidth,
30.0)];
[Two setTitle:@"2" forState:UIControlStateNormal];
[Two addTarget:self
action:@selector(digitalPressed:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:Two];

//第三个按钮
Three = [FUIButton buttonWithType:UIButtonTypeCustom];
[Three setFrame:CGRectMake(2 * btnWidth + 5.0 + 2 * btnXInterval,
100.0,
btnWidth,
30.0)];
[Three setTitle:@"3" forState:UIControlStateNormal];
[Three addTarget:self
action:@selector(digitalPressed:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:Three];


//加号按钮
Plus = [FUIButton buttonWithType:UIButtonTypeCustom];
[Plus setFrame:CGRectMake(3 * btnWidth + 5.0 + 3 * btnXInterval,
100.0,
btnWidth,
30.0)];
[Plus setTitle:@"+" forState:UIControlStateNormal];
[Plus addTarget:self
action:@selector(operatePressed:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:Plus];



//第四个按钮
Four = [FUIButton buttonWithType:UIButtonTypeCustom];
[Four setFrame:CGRectMake(5.0,
135.0,
btnWidth,
30.0)];
[Four setTitle:@"4" forState:UIControlStateNormal];
[Four addTarget:self
action:@selector(digitalPressed:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:Four];

//第五个按钮
Five = [FUIButton buttonWithType:UIButtonTypeCustom];
[Five setFrame:CGRectMake(btnWidth + 5.0 + btnXInterval,
135.0,
btnWidth,
30.0)];
[Five setTitle:@"5" forState:UIControlStateNormal];
[Five addTarget:self
action:@selector(digitalPressed:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:Five];

//第六个按钮
Six = [FUIButton buttonWithType:UIButtonTypeCustom];
[Six setFrame:CGRectMake(2 * btnWidth + 5.0 + 2 * btnXInterval,
135.0,
btnWidth,
30.0)];
[Six setTitle:@"6" forState:UIControlStateNormal];
[Six addTarget:self
action:@selector(digitalPressed:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:Six];

//减号按钮
Minus = [FUIButton buttonWithType:UIButtonTypeCustom];
[Minus setFrame:CGRectMake(3 * btnWidth + 5.0 + 3 * btnXInterval,
135.0,
btnWidth,
30.0)];
[Minus setTitle:@"-" forState:UIControlStateNormal];
[Minus addTarget:self
action:@selector(operatePressed:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:Minus];


//第七个按钮
Seven = [FUIButton buttonWithType:UIButtonTypeCustom];
[Seven setFrame:CGRectMake(5.0,
170.0,
btnWidth,
30.0)];
[Seven setTitle:@"7" forState:UIControlStateNormal];
[Seven addTarget:self
action:@selector(digitalPressed:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:Seven];

//第八个按钮
Eight = [FUIButton buttonWithType:UIButtonTypeCustom];
[Eight setFrame:CGRectMake(btnWidth + 5.0 + btnXInterval,
170.0,
btnWidth,
30.0)];
[Eight setTitle:@"8" forState:UIControlStateNormal];
[Eight addTarget:self
action:@selector(digitalPressed:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:Eight];

//第九个按钮
Nine = [FUIButton buttonWithType:UIButtonTypeCustom];
[Nine setFrame:CGRectMake(2 * btnWidth + 5.0 + 2 * btnXInterval,
170.0,
btnWidth,
30.0)];
[Nine setTitle:@"9" forState:UIControlStateNormal];
[Nine addTarget:self
action:@selector(digitalPressed:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:Nine];

//乘号按钮
Multiply = [FUIButton buttonWithType:UIButtonTypeCustom];
[Multiply setFrame:CGRectMake(3 * btnWidth + 5.0 + 3 * btnXInterval,
170.0,
btnWidth,
30.0)];
[Multiply setTitle:@"*" forState:UIControlStateNormal];
[Multiply addTarget:self
action:@selector(operatePressed:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:Multiply];

//小数点按钮
Decimal = [FUIButton buttonWithType:UIButtonTypeCustom];
[Decimal setFrame:CGRectMake(5.0,
205.0,
btnWidth,
30.0)];
[Decimal setTitle:@"." forState:UIControlStateNormal];
[Decimal addTarget:self
action:@selector(digitalPressed:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:Decimal];


//第十个按钮
Ten = [FUIButton buttonWithType:UIButtonTypeCustom];
[Ten setFrame:CGRectMake(btnWidth + 5.0 + btnXInterval,
205.0,
btnWidth,
30.0)];
[Ten setTitle:@"0" forState:UIControlStateNormal];
[Ten addTarget:self
action:@selector(digitalPressed:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:Ten];

//等号按钮
Equal = [FUIButton buttonWithType:UIButtonTypeCustom];
[Equal setFrame:CGRectMake(2 * btnWidth + 5.0 + 2 * btnXInterval,
205.0,
btnWidth,
30.0)];
[Equal setTitle:@"=" forState:UIControlStateNormal];
[Equal addTarget:self
action:@selector(enterPressed:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:Equal];

//除号按钮
Division = [FUIButton buttonWithType:UIButtonTypeCustom];
[Division setFrame:CGRectMake(3 * btnWidth + 5.0 + 3 * btnXInterval,
205.0,
btnWidth,
30.0)];
[Division setTitle:@"/" forState:UIControlStateNormal];
[Division addTarget:self
action:@selector(operatePressed:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:Division];

[self decorationButton:One];
[self decorationButton:Two];
[self decorationButton:Three];
[self decorationButton:Four];
[self decorationButton:Five];
[self decorationButton:Six];
[self decorationButton:Seven];
[self decorationButton:Eight];
[self decorationButton:Nine];
[self decorationButton:Ten];
[self decorationButton:Zero];
[self decorationButton:Equal];
[self decorationButton:Decimal];
[self decorationButton:Plus];
[self decorationButton:Minus];
[self decorationButton:Multiply];
[self decorationButton:Division];
}

-(void) viewDidAppear:(BOOL)animated
{
[self.navigationController.navigationBar configureFlatNavigationBarWithColor:[UIColor midnightBlueColor]];
}

- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

-(void) decorationButton:(FUIButton *)btn{
btn.buttonColor = [UIColor turquoiseColor];
btn.shadowColor = [UIColor greenSeaColor];
btn.shadowHeight = 3.0f;
btn.cornerRadius = 6.0f;
btn.titleLabel.font = [UIFont boldFlatFontOfSize:16];
[btn setTitleColor:[UIColor cloudsColor] forState:UIControlStateNormal];
[btn setTitleColor:[UIColor cloudsColor] forState:UIControlStateHighlighted];
}

//-(CalculatorBrain *) brain
//{
// if(!self.brain)
// {
// self.brain = [[CalculatorBrain alloc] init];
// }
// return self.brain;
//}

//数字键按下操作
-(void) digitalPressed:(id)sender
{
if(self.isEntering == YES)
{
//如果之前输入的数字少于12位
if([self.display.text length] < 12)
{
//把按到的数字转换成字符串加到原来显示的字符串的末尾,并更新显示
self.display.text = [self.display.text stringByAppendingString:[sender currentTitle]];
}
else//如果就输入的数字大于等于12位,不操作,直接返回
{
return;
}
}
else//如果之前不是输入状态,即现在输入的是数字的第一位(最高位)
{
//显示输入的数字
self.display.text = [sender currentTitle];
//把输入的状态改为YES
self.isEntering = YES;
}
}

//操作符按下操作
-(void) operatePressed:(id)sender
{
//输入状态改为NO
self.isEntering = NO;
//取到显示的数字。转换为double
double num = [self.display.text doubleValue];
//把数字和操作符发送给CalculatorBrain的operate接收器
[self.brain operate:num :[sender currentTitle]];
}

//等于号按下操作
-(void) enterPressed:(id)sender
{
//把输入状态改为NO
self.isEntering = NO;
//取到显示的数字,转换为double
double num = [self.display.text doubleValue];
//把数字发送给CalculateBrain的calcute接收器,并接受返回的结果
double result = [self.brain caculate:num];
//把结果显示在屏幕上
self.display.text = [NSString stringWithFormat:@"%g",result];
}

-(BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
return (toInterfaceOrientation != UIDeviceOrientationPortraitUpsideDown);
}

//-(BOOL) shouldAutorotate
//{
// return YES;
//}
//
//-(NSUInteger) supportedInterfaceOrientations
//{
// return UIInterfaceOrientationMaskLandscape;
//}


@end

里面使用到了FlatUI,可以自行google搜索然后下载。

UIViewController的shouldAutorotateToInterfaceOrientation方法被deprecated。在ios6里,是使用supportedInterfaceOrientations and shouldAutorotate 2个方法来代替shouldAutorotateToInterfaceOrientation。注意:为了向后兼容iOS 4 and 5,还是需要在你的app里保留shouldAutorotateToInterfaceOrientation。

ios 4 and 5, 如果没有重写shouldAutorotateToInterfaceOrientation,那么对于iphone来讲,by default是只支持portrait,不能旋转。

ios 6, 如果没有重写shouldAutorotate and supportedInterfaceOrientations,by default, iphone则是”可以旋转,支持非upside down的方向”,而ipad是”可以选择,支持所有方向”

举个例子;

ios 4 and 5, iphone device, 若要”可以旋转,支持非upside down的方向”,则可以在view controller里

1
2
3
4
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation 
{
return (interfaceOrientation != UIDeviceOrientationPortraitUpsideDown);
}

ios 6, iphone device, 若要“不能旋转,只支持portait”,则可以在view controller里

1
2
3
4
- (BOOL)shouldAutorotate
{
return NO;
}

ios 6, ipad device, 若要“可以旋转,只支持landscape”,则可以在view controller里

1
2
3
4
5
6
7
8
9
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscape;
}

- (BOOL)shouldAutorotate
{
return YES;
}

在iOS 4 and 5,都是由具体的view controller来决定对应的view的orientation设置。而在iOS 6,则是由top-most controller来决定view的orientation设置。

举个例子:你的app的rootViewController是navigation controller “nav”, 在”nav”里的stack依次是:main view -> sub view > sub sub view,而main view里有一个button会present modal view “modal view”.

那么ios 4 and 5,在ipad里,如果你要上述view都仅支持横屏orientation,你需要在上面的main view, sub view, sub sub view, model view里都添加

1
2
3
4
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation 
{
return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft || interfaceOrientation==UIInterfaceOrientationLandscapeRight);
}

而对于iOS6, 由于是由top-most controller来设置orientation,因此你在main view, sub view, sub sub view里添加下面的代码是没有任何效果的,而应该是在nav controller里添加下列代码。而modal view则不是在nav container里,因此你也需要在modal view里也添加下列代码。

1
2
3
4
5
6
7
8
9
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscape;
}

- (BOOL)shouldAutorotate
{
return YES;
}

注意几点:

1,你需要自定义一个UINavigationController的子类for “nav controller”,这样才可以添加上述代码。

2,和navigation controller类似,tab controller里的各个view的orientation设置应该放在tab controller里

ios6的top-most controller决定orientation设置,导致这样一个问题:在 top-most controller里的views无法拥有不相同的orientation设置。例如:for iphone, 在nav controller里,你有main view, sub view and sub sub view,前2个都只能打竖,而sub sub view是用来播放video,可以打横打竖。那么在ios 4 and 5里可以通过在main view and sub view的shouldAutorotateToInterfaceOrientation里设置只能打竖,而在sub sub view的shouldAutorotateToInterfaceOrientation设置打竖打横即可。而在ios 6里则无法实现这种效果,因为在main view, sub view and sub sub view的orientation设置是无效的,只能够在nav controller里设置。那么你可能想着用下列代码在nav controller里控制哪个view打竖,哪个view打横

1
2
3
4
5
6
7
-(NSUInteger)supportedInterfaceOrientations
{
if([[self topViewController] isKindOfClass:[SubSubView class]])
return UIInterfaceOrientationMaskAllButUpsideDown;
else
return UIInterfaceOrientationMaskPortrait;
}

是的,这样可以使得在main view and sub view里无法打横,而sub sub view横竖都行。但问题来了,如果在sub sub view时打横,然后back to sub view,那么sub view是打横显示的!

目前想到的解决方法只能是把sub sub view脱离nav controller,以modal view方式来显示。这样就可以在modal view里设置打横打竖,而在nav controller里设置只打竖。

1,说了那么多,其实如果你的app的所有view的orientation的设置是统一的,那么你可以简单的在plist file里设置即可,不用添加上面的代码。而如果你添加了上面的代码,就会覆盖plist里orientation的设置。

2,iOS 6, 当view controller present时,不会call willRotateToInterfaceOrientation:duration:, willAnimateRotationToInterfaceOrientation:duration:, and didRotateFromInterfaceOrientation: methods,只有在发生rotate的时候才会call

0%