zoukankan      html  css  js  c++  java
  • 三十而立,从零开始学ios开发(十九):Application Settings and User Defaults(上)

    三十而立,从零开始学ios开发(十九):Application Settings and User Defaults(上)

    在iphone和ipad中,有一个东西大家一定很熟悉,那个东西就是Settings

    这次要学习的东西说白了很简单,就是学习如何在Settings中对一个app的某些属性进行设置,反过来,在app中更改了一些属性值,也会反应到Settings中,这个功能很常用,实现起来也相对简单,但是内容还是比较多的。

    首先还是对Settings进行一个简单的说明,虽然我们经常打开Settings,但是很少对Settings进行过仔细的研究,不过作为一名ios的开发人员,有这个必要对Settings进行一番探索,看看Settings里面到底包含了哪些东西,这些东西又是什么。

    首先我们打开Settings,可以看到Settings的主界面(画面截取自iphone模拟器)

    可以看到,Settings的主界面是一个Table View,Style的属性是Grouped。除了常见的General、Airplane Mode、Wi-Fi、Notifications等(后面3个未在截图中出现,打开你的iphone,Settings里面会有这些选项),iphone为每个有需要的app创建了一个cell,例如Twitter、Facebook等。点击Twitter,即可以看到该app所包含的一些设置项。

    Settings中的设置项是有限制的,一共可以包含以下4类:Text Field,Switch,Slider, TableView的checklist。
     

    大家应该看出,Settings其实是一个Navigation Controller,View与View之间是继承关系。

    在说一下iOS中是以什么方式来操作Settings的,这个东西叫做NSUserDefaults,它可以方便的在app与Settings进行交互,保存更新值。

    好了,简单的介绍就到这里,下面我们开始这次的例子,作完这个例子,相信会对Settings有更深的了解。

    1)创建一个工程,左边选择Application,右边选择Utility Application,点击Next按钮

    在Product Name中输入AppSettings,然后选中Use Storyboards,点击Next按钮

    点击Create按钮完成创建

    观察一下程序为我们自动创建的工程项目文件,里面有一个很常见的BIDAppDelegate,另外我们刚才选中的Use Storyboard,因此有一个MainStoryboard.storyboard存在,除了这些,另外还有4个文件,分别是BIDMainViewController和BIDFlipsideViewController,这个是Utility Application模板自动为我们创建的。

    由于这是我们第一次使用Utility Application,因此在这里做一些简单的介绍,Utility Application会自动帮我们创建2个view,第一个view叫做main view,第二个view叫做flipside view,在main view上面有一个information button,点击这个information button就会切换到flipside view。在flipside view的navigator bar上有一个Done按钮,点击这个Done按钮,就会切换回main view。在Project navigator中选中MainStoryboard.storyboard,就会看见2这个view,而这2个view就分别对应着BIDMainViewController和BIDFlipsideViewController。

    好了,下面我们需要添加一个Settings Bundle,如果你的一个app需要在Settings中进行设置,那么在你的项目中一定要加Settings Bundle,这个东西的作用就是当你把你的app安装到iphone上后,它会自动在Settings中添加与这个项目相关的一个条目(cell)用于设置。

    2)添加Settings Bundle
    选择File>New>File...,在左边的选择Resource,右边选择Settings Bundle,点击Next按钮

    保留默认的名字,点击Create按钮完成添加

    展开Settings.bundle,有2个默认项,一个是en.lproj文件夹(这个暂时忽略,用户本地化程序的,现在无需理会),另一个是Root.plist,从后缀名我们可以断定,Settings中的项是基于Property list,也就是一个xml文件。

    选中Property list,在editor pane中会显示如下内容

    我们改变一下Root.list的显示方式,在editor pane的任何地方点击鼠标右键,然后在弹出的菜单中选择“Show Raw Keys/Values”

    然后Root.plist会显示如下样子

    最直观的发现是2个Key的名字变了,“Preference Items”变成了“PreferenceSpecifiers”,“Strings Filename”变成了“StringsTable”,其实内容并没有改变,只是显示的方式不同,这个看个人喜好吧,前者名字比较通俗移动,后者名字更贴近实现情况。

    另外,我们不必过多关注StringsTable,这个值是用在localization的时候的,因此在这里你可以直接删了它。

    3)添加Settings中的控件
    添加Text Field

    接着我们展开PerferenceSpecifiers,里面有4个默认的Item,这些都是系统帮我们生成的,但是这些并不是我们这个例子想要的,所以,我们删除Item 1、Item 2、Item 3,只保留Item 0。

    (上图中我把StringsTable也删了)

    接着我们展开Item 0,看看里面有些什么的东西
     
    里面有一个Title和Type键值对(也就是Dictionary),我们把注意力集中到Type,它的值是PSGroupSpecifier,说明这个item是一个group类型,在这个item之后的所有同级item,都是属于这个group的,直到下一个group类型出现。在PreferenceSpecifiers下,必须要有至少一个group存在。Title用于设置这个group的名称,这个属性是可以省略的。

    在观察一下Item 0的显示方式:Item 0 (Group - Group),括号中第一个Group是只这个item的类型为PSGroupSpecifier,第二个Group是这个item的Title值。我们修改一下Title的值,设置其为“General Info”

    设置完成后,你会发现Item 0的显示变成了:Item 0 (Group - General Info)

    PSGroupSpecifier并不是一个实际可以操作的类型,它的作用仅仅是把几个相关的属性包含在一起,形成一个table view中的group,下面我们添加可操作的节点。

    首先将Item 0合拢,然后鼠标选中Item 0的整行,单击键盘上的return键,一个新的Item(Item 1)出现在Item 0的下方,并有一个列表弹出,让你选择这个Item的类型

    在这里我们选择Text Field,创建完Item 1后,展开Item 1

    可以看到一共有3个键值对,Type的类型为PSTextFieldSpecifier,另外2个分别是Title和Key,将Title赋值为“Username”,将Key赋值为“username”(注意大小写)。这里的Key是用于保存和获取Item 1的值的,每一个Item你可以理解为是一个Dictionary,一个Dictionary就是一个键值对,那么这个Key的作用就是获取或者保存指定的值。(Item 0中并没有Key,这个是因为Item 0的类型是PSGroupSpecifier,没有需要保存的值)

    在这篇文章刚刚开始的时候,我们提到过NSUserDefaultes,NSUserDefaultes就是使用这个key来获得item的值的,也是用这个key来保存更新值的。

    最后我们为Item 1再添加2个属性,选中Item 1中最后一行(Key所在的行),然后按下键盘上的return,一个新的行出现在Key的下面,另外有一个list让你选中Key的名字,这里我们选择“AutocapitalizationType”,然后设置值为“None”。同样的方法再添加一次,这次选择“AutocorrectionType”,然后设置值为“No”


    “AutocapitalizationType”的意思是是否自动完成输入,也就是说你输入一个单词的前几个字,系统会出现一个列表,列表里面会有相关的词汇,你只有直接选中,就可以完成输入了,这里我们设置为“None”,无需自动完成。
    “AutocorrectionType”的意思是自动纠正拼写,就是系统帮你建成你输入的单词是否有拼写错误,我们同样把这个功能关掉了。

    好了,保存一下Root.plist。

    到此为止,我们可以试着编译运行程序了,为了使程序能够在Settings中突出显示(好找一点),我们为程序添加一个图标,先下载一个图标icon,然后在Project navigator中选中根节点AppSettings,然后在左边选择TARGETS下的AppSettings,打开Summary tab,展开iPhone / iPod Deployment Info,找到App Icons

    将icon拖入到左边的图标中(右边的是视网膜屏用的图标,我们这里没有提供这个图标,所以就空着吧)

    好了,编译运行一下程序,程序启动后,按Home键回到桌面,然后进入Settings,在Settings主界面的最下面,可以找到我们创建的AppSettings程序图标

    点击该图标,就会进入这个程序的设置

    上图中我们可以看到在Root.plist中我们设置的项还原出来的结果,首先是一个Group,Group的Title为General Info,然后在Group中是一个TextField的项,它的Title是Username。

    OK,到此位置,你应该对Settings有所了解,知道里面的项是怎么产生的,每个项的属性的作用等等。下面我们接着添加更多的不同类型的项,全面的对Settings进行操作。

    3)添加其他的项
    添加Secure Text Field
    先将Item 1闭合,然后选中它,按键盘上的command+C,command+V,复制粘贴一份Item,一个新的Item 2会出现在Item 1的下面

    展开Item 2,并将其下的属性设置为下面的样子

    如上图所示,Item 2是用于接收密码的,它的Type类型还是为PSTextFieldSpecifier,将它的Title设为Password,Key为password,这里多了一个新的属性叫做IsSecure,当将它的值设为YES时,那么在文本框中内容就会以密码的方式显示。(你可以编译运行一下,看看是不是这个密码框的效果)

    添加Multivalue Field
    Multivalue Field会产生一个带箭头的cell,点击该行后,会跳转到下一个table view,下一个table view中会包含多个选项,用户在多个选项中选取其中的一个,然后返回到前一个view,该cell中的内容就是用户选取的内容。

    我们合拢Item 2,然后选中Item 2,按return键新建Item 3,选择Item 3的类型为Multi Value

    展开Item 3,可以看到,Type的类型为PSMultiValueSpecifier。将Title设置为Protocol,Key设置为protocol。

    ok,下面我们会添加一组Titles和Values,Titles保存每一个选项的显示值,Values中保存每一个选项的id,他们是一一对应的。保持Item 3展开的形态,然后选中Item 3,按return键(在Item展开的形态下选中并按retuan键,是添加当前项的子项;在Item闭合的形态下选中帮按return键,是添加同级系项),会新建一个Item 3的子项并出现一个下拉框选中子项的类型,在这里我们选择Titles。

    重复上面的动作,再创建一个Item 3的子项,并选择Values。

    选中Titles,然后点击加号,添加一个子项,赋值为HTTP

    重复这个动作,添加以下值SMTP,NNTP,IMAP,POP3

    使用同样的方法,为Values添加项,添加完后的样子如下

    Values和Titles不同之处在于一个是小写一个是大写,当然,如果你全部使用大写或者小写也是没有问题的,看个人喜好了。

    编译运行一个程序

    在Passwrod中输入密码,显示的是一个一个的小圆点。Protocol项的右边多了一个箭头,点击该项,跳转到下一个view

    这里就是我们刚才添加的Titles的内容,我们随意选中一个

    选中SMTP后的状态,然后我们点击左上角的AppSettings按钮返回上一级view

    刚才选中的SMTP就显示在Protocol项的右边。

    添加Toggle Switch Settings
    Toggle Switch很简单,就是一个switch,可以选择打开或者关闭,这个类型的目的是设置一个bool值。

    合拢Item 3,然后选中,按return键,添加一个Item 4,并设置Item 4的类型为Toggle Switch,展开Item 4,设置Title为“Warp Drive”,Key为“warp”,将DefaultValue的值改为YES

    好了,编译运行一下,一个Switch出现在Protocol的下面

    添加Slider Setting
    Slider我们已经熟知,在Settings中,一个Slider可以在其2端各放置一副图片(但我发现在iphone中放置图片的例子不多),Slider本身没有带文字说明,我们也不可以在Settings中放置一个label告知用户这个slider的作用是什么,因此这里的解决方案是添加一个新的Group,然后为Group添加文字说明,告知用户slider的作用。

    合拢Item 4,并选中,按return键,添加Item 5,选择Item 5的类型为Group,并设置Title值为“Warp Factor”

    合拢Item 5,并选中,按return键,添加Item 6,选择Item 6的类型为Slider,根据下图设置Item 6的属性

    编译运行一下程序,效果如下


    刚才我们说了,Slider的两端可以各添加一个图片(图片的大小为21 * 21 pixel),我们现在就来添加,先下载这里的图片

    为slider添加image的方法有些特殊,我们并不是直接将图片拖到project navigator中,然后放到slider中,Settings并没有为我们提供这样的方法。我们使用别的方法添加,首先在Project navigator中鼠标右击Settings.bundle,选择Show in Finder

    在Finder中右击Settings.bundle,选择“显示包内容”(Show Package Contents)

    然后将2张图片复制进包里面,然后在Project navigator中也能够看见这2张图片了

    接着打开Root.plist,在Item 6中添加2项Max Value Image Filename和Min Value Image Filename,为Max赋值rabbit,为Min赋值turtle
     

    再次编译运行,2张图片出现在slider的左右2边。

    添加一个Child Settings View
    这个意思就是单击table view上的一个cell,跳转到另一个Settings view。经过上面的一些讲解,我们实际上都是在对plist进行操作,由此可以推断出,所有的Settings都是以一个一个的plist文件,因此如果要跳转到另一个Settings view,那必须包含另一个plist,根据这个思路,我们进行下面的操作。

    我们再创建一个Group,创建在Item 6的下面,并命名为Title

    然后我们在Item 7的下面添加Item 8,按照之前的方式,现在我们应该设置Item 8的类型了,但是在默认的下拉框中并没有我们需要的类型(Child Pane),不急,我们展开Item 8(在这里确保你选择了Show Raw Keys/Values),然后点击其Type行的Value列最后边的按钮,会出现一个下拉框

    在下拉框中选择PSChildPaneSpecifier,这样Item 8的类型就是一个Child Pane了。

    接着设置Title为“More Settings”,Key空着,因为这是一个起到导航作用的Item,我们无需得到它的值,也就不需要它的Key了。

    再接着我们选中最后一行Key,按return键添加一个,在下拉菜单中选择File

    我们需要关联一个plist文件,这样就可以导航到另一个Settings view了,下载这里的More.plist,还记得刚才我们是如何为slider添加2个图片的吗?在Project navigator中鼠标右击Settings.bundle,然后选择Show in Finder,在Finder中鼠标右击Setting.bundle,选择Show Package Contents,将More.plist复制进去,这样在Project navigator中就出现More.plist了。

    由于More.plist是现成帮我们做好了,因为我们不需对其进行任何操作,直接用就可以了。编译运行程序

    Settings view的底部多了我们刚才添加的More Settings,点击More Settings,跳转到More.plist的Settings view

    可以看到在More中前4个都是Text Field,最后一个是Mulitvalue Field,之后大家随便点吧,反正也就这些东西了。

    4)总结
    到此为止,所有可以在Settings中添加的控件都已经介绍了,总类不是很多,一共6种,而对于Settings的操作,也就是对一个plist文件的操作,iOS系统会自动将plist中的内容反应到Settings上去,我们只需针对plist进行操作,就可以很简单的完成Settings view的设置。在下一篇中,我们将把Settings中的值和真正的app程序连接起来,在Settings中设置值后,在app中会反应出来,在app中对一个值进行更改,在Settings中的这个值同样会被更新,连接这两个东西的桥梁就是之前提到的NSUserDefaults,我们在下一篇中进行详细的介绍,谢谢!

    IOS: 状态栏提示控件的实现原理

    状态栏提示控件的实现原理

      

      现在很多流行的软件都加入了状态栏提示的功能,比如手机qq,微信等,今天我们就一起来看看状态栏提示控件的原理与实现。

    一、状态栏提示的实现原理

      不知道大家看到状态栏提示控件,第一感觉它是怎么实现的呢?

      我们知道即使平时写的view是充满全屏的,也始终不会显示到statusBar的上层的。也就是说statusBar应该是一个特殊的view,始终位于程序的topLevel,这就容易联想到UIKit中一个特殊的view-----UIWindow。UIWindow有一个windowLevel的属性刚好能实现一直保持在上层的功能,于是方向就比较明确了。我较早写的两篇博客UIWindowLevel详解以及关于UIWindow的一点儿思考中对windowLevel有过详细的介绍和验证。

      确定了使用UIWindow来实现状态栏提示控件,好像问题就全部解决了,真的是这样吗?

      如果你的程序仅仅支持portrait方向的话,那么最主要的功能就结束了,剩下的事情就是文本框布局和简单动画的实现了。但如果你的控件要支持其他三个方向的话,就还需要处理window的旋转。

      IOS中完整的旋转流程如下: 设备检测到方向旋转->UIApplication收到旋转事件->通知window设备发生了旋转->window通知它的rootViewController进行旋转->viewController会调整自身view的transform。观察发现自始至终window本身的位置和方向是没有发生变化的,也就是说如果自己创建一个window用于展示提示,我们需要自己处理该window的旋转,根据不同的方向调整window的位置和transform。

      综上要实现状态栏提示控件我们需要做以下两件事:

    1、创建一个UIWindow,指定它的frame为statusBar的frame,并且设置该window的windowLevel级别略高于statusBar的windowLevel。

    2、注册系统的旋转通知,监测设备方向变化,根据当前设备的方向做出相应的调整。

      

      整个过程中主要用到了UIWindow和transfrom的知识,这两部分知识我前面写的博客都有涉及,难点主要集中在自己旋转window这一块。

    二、Window的旋转

      UIKit通过UIWindow和UIViewContoller为我们提供了一套旋转支持的框架,在方向变化以后viewController中view的坐标系统就已经被转到正确的方向了,我们只需要简单的重新布局就可以了。我们现在是直接通过UIWindow实现状态栏提示控件,因此需要自己完成对该window进行旋转的操作。

      我们知道对当前view设置的transform是针对它的父view的,window本身就是一种特殊的view。你可能会疑问window不就是最底层的view,它还有父view吗?

      答案是YES,不信的话你可以打印一下window的superView看看。window默认方向是portrait方向,向下y坐标增加,向右x坐标增加。因此Portrait方向我们只需要向普通的view那样布局就可以了,其它几个方向我们就需要用到transform和设置位置来搞定了。

      下面我们看一看从Portrait方向转到landscapeRight方向的图示:

      上图中从左到右展示了如何将初始位置(Portrait方向),旋转到目标位置(landscapeRight方向)的过程。

    1、原始window位置位于屏幕最上方(与statusBar的位置一样)。

    2、首先我们对这个window做顺时针90°旋转,变化后到达绿色绘制位置。

    3、接着我们再修改window的center到屏幕最右边并且上下居中,达到红色虚线所示的位置。

    4、最后对该window的bound进行设置,使该window充满屏幕最右边的区域。注意这个时候由于window的transform已经转了90°,所以设置时width代表着高度,height代表这宽度。

    下面是完整的处理旋转到四个方向的代码:

    复制代码
    - (void)updateOrientation:(NSNotification*)noti
    {
        UIInterfaceOrientation newOrientation = [[noti.userInfo valueForKey:UIApplicationStatusBarOrientationUserInfoKey] integerValue];
        NSLog(@"new orientation: %d", newOrientation);
        
        switch (newOrientation) {
            case UIInterfaceOrientationPortrait:
            {
                self.transform = CGAffineTransformIdentity;
                self.frame = CGRectMake(0, 0, SCREEN_WIDTH, HEIGHT);
                
                break;
            }
            case UIInterfaceOrientationPortraitUpsideDown:
            {
                // 先转矩阵,坐标系统落在屏幕有右下角,朝上是y,朝左是x
                self.transform = CGAffineTransformMakeRotation(M_PI);
                self.center = CGPointMake(SCREEN_WIDTH / 2, SCREEN_HEIGHT - HEIGHT / 2);
                self.bounds = CGRectMake(0, 0, SCREEN_WIDTH, HEIGHT);
                
                break;
            }
            case UIInterfaceOrientationLandscapeLeft:
            {
                self.transform = CGAffineTransformMakeRotation(-M_PI_2);
                // 这个时候坐标轴已经转了90°,调整x相当于调节竖向调节,y相当于横向调节
                self.center = CGPointMake(HEIGHT / 2, [UIScreen mainScreen].bounds.size.height / 2);
                self.bounds = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.height, HEIGHT);
                
                break;
            }
            case UIInterfaceOrientationLandscapeRight:
            {
                // 先设置transform,在设置位置和大小
                self.transform = CGAffineTransformMakeRotation(M_PI_2);
                self.center = CGPointMake(SCREEN_WIDTH - HEIGHT / 2, SCREEN_HEIGHT / 2);
                self.bounds = CGRectMake(0, 0, SCREEN_HEIGHT, HEIGHT);
                
                break;
            }
            default:
                break;
        }
    }
    复制代码

      

    三、状态栏提示控件源码

    讲了那么多,说好的控件呢?

      下面是完整的控件代码:

     View Code SvStatusBarTipsWindow.h
    //
    //  SvStatusBarTipsWindow.h
    //  SvStatusBarTips
    //
    //  Created by  maple on 4/21/13.
    //  Copyright (c) 2013 maple. All rights reserved.
    //
    
    #import <UIKit/UIKit.h>
    
    @interface SvStatusBarTipsWindow : UIWindow
    
    /*
     * @brief get the singleton tips window
     */
    + (SvStatusBarTipsWindow*)shareTipsWindow;
    
    /*
     * @brief show tips message on statusBar
     */
    - (void)showTips:(NSString*)tips;
    
    /*
     * @brief show tips message on statusBar
     */
    - (void)showTips:(NSString*)tips hideAfterDelay:(NSInteger)seconds;
    
    /*
     * @brief show tips icon and message on statusBar
     */
    - (void)showTipsWithImage:(UIImage*)tipsIcon message:(NSString*)message;
    
    /*
     * @brief show tips icon and message on statusBar
     */
    - (void)showTipsWithImage:(UIImage*)tipsIcon message:(NSString*)message hideAfterDelay:(NSInteger)seconds;
    
    
    /*
     * @brief hide tips window
     */
    - (void)hideTips;
    
    @end
     SvStatusBarTipsWindow.m
    //
    //  SvStatusBarTipsWindow.m
    //  SvStatusBarTips
    //
    //  Created by  maple on 4/21/13.
    //  Copyright (c) 2013 maple. All rights reserved.
    //
    
    #import "SvStatusBarTipsWindow.h"
    
    #define SCREEN_WIDTH  ([UIScreen mainScreen].bounds.size.width)
    #define SCREEN_HEIGHT ([UIScreen mainScreen].bounds.size.height)
    
    #define HEIGHT   20
    
    #define ICON_WIDTH 20
    
    #define TIPMESSAGE_RIGHT_MARGIN 20
    #define ICON_RIGHT_MARGIN       5
    
    
    @interface SvStatusBarTipsWindow () {
        UILabel     *_tipsLbl;
        UIImageView *_tipsIcon;
        
        NSTimer     *_hideTimer;
    }
    
    @property (nonatomic, copy)     NSString *tipsMessage;
    
    @end
    
    
    
    @implementation SvStatusBarTipsWindow
    
    @synthesize tipsMessage;
    
    
    static SvStatusBarTipsWindow *tipsWindow = nil;
    
    + (SvStatusBarTipsWindow*)shareTipsWindow
    {
        if (!tipsWindow) {
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                tipsWindow = [[super allocWithZone:NULL] init];
            });
        }
        
        return tipsWindow;
    }
    
    + (id)copyWithZone:(NSZone *)zone
    {
        return [[self shareTipsWindow] retain];
    }
    
    + (id)allocWithZone:(NSZone *)zone
    {
        return [[self shareTipsWindow] retain];
    }
    
    - (id)retain
    {
        return tipsWindow;
    }
    
    - (oneway void)release
    {
        return;
    }
    
    - (id)autorelease
    {
        return tipsWindow;
    }
    
    - (id)init
    {
        CGRect frame = [UIApplication sharedApplication].statusBarFrame;
        self = [super initWithFrame:frame];
        if (self) {
            
            self.autoresizingMask = UIViewAutoresizingFlexibleWidth;
            self.windowLevel = UIWindowLevelStatusBar + 10;
            self.backgroundColor = [UIColor clearColor];
            
            _tipsIcon = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, ICON_WIDTH, ICON_WIDTH)];
            _tipsIcon.autoresizingMask = UIViewAutoresizingFlexibleHeight;
            _tipsIcon.backgroundColor = [UIColor redColor];
            [self addSubview:_tipsIcon];
            [_tipsIcon release];
            
            _tipsLbl = [[UILabel alloc] initWithFrame:self.bounds];
            #ifdef NSTextAlignmentRight
                _tipsLbl.textAlignment = NSTextAlignmentLeft;
                _tipsLbl.lineBreakMode = NSLineBreakByTruncatingTail;
            #else
                _tipsLbl.textAlignment = 0; // means UITextAlignmentLeft
                _tipsLbl.lineBreakMode = 4; //UILineBreakModeTailTruncation;
            #endif
            _tipsLbl.textColor = [UIColor whiteColor];
            _tipsLbl.font = [UIFont systemFontOfSize:12];
            _tipsLbl.backgroundColor = [UIColor blackColor];
            _tipsLbl.autoresizingMask = UIViewAutoresizingFlexibleHeight;
            [self addSubview:_tipsLbl];
            [_tipsLbl release];
            
            [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateOrientation:) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];
        }
        
        return self;
    }
    
    - (void)dealloc
    {
        [[NSNotificationCenter defaultCenter] removeObserver:self];
    
        [tipsMessage release];
        
        [super dealloc];
    }
    
    #pragma mark -
    #pragma mark Notification Handle
    
    - (void)updateOrientation:(NSNotification*)noti
    {
        UIInterfaceOrientation newOrientation = [[noti.userInfo valueForKey:UIApplicationStatusBarOrientationUserInfoKey] integerValue];
        NSLog(@"new orientation: %d", newOrientation);
        
        switch (newOrientation) {
            case UIInterfaceOrientationPortrait:
            {
                self.transform = CGAffineTransformIdentity;
                self.frame = CGRectMake(0, 0, SCREEN_WIDTH, HEIGHT);
                
                break;
            }
            case UIInterfaceOrientationPortraitUpsideDown:
            {
                // 先转矩阵,坐标系统落在屏幕有右下角,朝上是y,朝左是x
                self.transform = CGAffineTransformMakeRotation(M_PI);
                self.center = CGPointMake(SCREEN_WIDTH / 2, SCREEN_HEIGHT - HEIGHT / 2);
                self.bounds = CGRectMake(0, 0, SCREEN_WIDTH, HEIGHT);
                
                break;
            }
            case UIInterfaceOrientationLandscapeLeft:
            {
                self.transform = CGAffineTransformMakeRotation(-M_PI_2);
                // 这个时候坐标轴已经转了90°,调整x相当于调节竖向调节,y相当于横向调节
                self.center = CGPointMake(HEIGHT / 2, [UIScreen mainScreen].bounds.size.height / 2);
                self.bounds = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.height, HEIGHT);
                
                break;
            }
            case UIInterfaceOrientationLandscapeRight:
            {
                // 先设置transform,在设置位置和大小
                self.transform = CGAffineTransformMakeRotation(M_PI_2);
                self.center = CGPointMake(SCREEN_WIDTH - HEIGHT / 2, SCREEN_HEIGHT / 2);
                self.bounds = CGRectMake(0, 0, SCREEN_HEIGHT, HEIGHT);
                
                break;
            }
            default:
                break;
        }
    }
    
    #pragma mark -
    #pragma mark Tips Method
    
    /*
     * @brief show tips message on statusBar
     */
    - (void)showTips:(NSString*)tips
    {
        if (_hideTimer) {
            [_hideTimer invalidate];
            [_hideTimer release];
        }
        
        _tipsIcon.image = nil;
        _tipsIcon.hidden = YES;
        
        CGSize size = [tips sizeWithFont:_tipsLbl.font constrainedToSize:CGSizeMake(320, 30)];
        size.width += TIPMESSAGE_RIGHT_MARGIN;
        if (size.width > self.bounds.size.width - ICON_WIDTH) {
            size.width = self.bounds.size.width - ICON_WIDTH;
        }
        
        _tipsLbl.frame = CGRectMake(self.bounds.size.width - size.width, 0, size.width, self.bounds.size.height);
        _tipsLbl.text = tips;
        
        [self makeKeyAndVisible];
    }
    
    - (void)showTips:(NSString*)tips hideAfterDelay:(NSInteger)seconds
    {
        [self showTips:tips];
        
        _hideTimer = [NSTimer scheduledTimerWithTimeInterval:seconds target:self selector:@selector(hideTips) userInfo:nil repeats:NO];
    }
    
    /*
     * @brief show tips icon and message on statusBar
     */
    - (void)showTipsWithImage:(UIImage*)tipsIconImage message:(NSString*)message
    {
        if (_hideTimer) {
            [_hideTimer invalidate];
            [_hideTimer release];
        }
        
        CGSize size = [message sizeWithFont:_tipsLbl.font constrainedToSize:self.bounds.size];
        size.width += TIPMESSAGE_RIGHT_MARGIN;
        if (size.width > self.bounds.size.width - ICON_WIDTH) {
            size.width = self.bounds.size.width - ICON_WIDTH;
        }
        
        _tipsLbl.frame = CGRectMake(self.bounds.size.width - size.width, 0, size.width, self.bounds.size.height);
        _tipsLbl.text = message;
        
        _tipsIcon.center = CGPointMake(self.bounds.size.width - _tipsLbl.bounds.size.width - ICON_WIDTH / 2 - ICON_RIGHT_MARGIN, self.bounds.size.height / 2);
        _tipsIcon.image = tipsIconImage;
        _tipsIcon.hidden = NO;
        
        [self makeKeyAndVisible];
    }
    
    - (void)showTipsWithImage:(UIImage*)tipsIconImage message:(NSString*)message hideAfterDelay:(NSInteger)seconds
    {
        [self showTipsWithImage:tipsIconImage message:message];
        
        _hideTimer = [NSTimer scheduledTimerWithTimeInterval:seconds target:self selector:@selector(hideTips) userInfo:nil repeats:NO];
    }
    
    /*
     * @brief hide tips window
     */
    - (void)hideTips
    {
        self.hidden = YES;
    }
    
    @end

      该状态栏提示控件实现了添加提示图标和提示文字,以及自动隐藏等功能。显示和隐藏动画实现起来比较简单,控件中就没有实现,大家可以根据需要随意发挥。该控件使用单例模式,接口非常简单,使用起来很方便。上面代码相信大家都能看得懂,这里就不展开讲了,有什么问题欢迎讨论。

    注: 转载请注明出去,有什么不对的地方,欢迎指正。

     
     
    分类: Source CodeUIKit
     
     
    分类: IOS
  • 相关阅读:
    matlab程序性能优化与混合编程技术介绍
    最大熵原理/最大熵原则/最大熵模型(the maximum entropy principle,MEP)
    马氏距离 Mahalanobis Distance
    时间序列分析
    Windows XP + Apache 2.2.4 + PHP 5.2.0 + MySQL 5.0.27 + Zend Optimizer 3.2.0环境配置方法
    栈应用——表达式求值
    Android实现模拟时钟(简单+漂亮)时针、分针、秒针
    基于循环链表的约瑟夫问题
    assert()详解
    Hadoop HPROF 的使用
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3109766.html
Copyright © 2011-2022 走看看