iOS开发之美(txt+pdf+epub+mobi电子书下载)


发布时间:2020-09-01 20:01:35

点击下载

作者:和凌志

出版社:电子工业出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

iOS开发之美

iOS开发之美试读:

前言

之所以写这本书,还得从我的个人经历说起。

在过去的十几年中,我一直在手机行业摸爬滚打。对手机软件平台的认识,源自我在Siemens Mobile担任软件架构师期间的工作和学习。也正是基于在手机软件行业的积累,才出版了《手机软件平台架构解析》一书。

在我看来,手机软件平台的分水岭是在2008年前后。在此之前,各个主流手机厂商都是在打造自己的手机平台,也曾出现过一些小公司所打造的第三方平台。打造一个可用的手机软件平台,绝非一件容易的事,尤其是在智能机时代。仅仅是解决一个触摸屏问题,就让很多手机厂商伤透了脑筋。如何打造一个手机软件平台,让第三方开发者能够在此之上轻松开发出一款App,这是所有手机软件架构师梦寐以求的向往。直到iPhone手机的出现,手机行业才为之一振。我们所期待的智能手机,就应该是这个样子。随着2008年10月iOS SDK的开放,开发者才意识到,原来App开发还可以这么玩!为什么将本书命名为“iOS开发之美”?

时至今日,业内还有多个手机软件平台。我心中很清楚,仅仅以iOS为美,肯定会招来各种非议。就我个人而言,我和我的团队在iOS平台上做开发,已经有六个年头了。这里,我只是将自己的工作心得写出来,供大家学习参考。

称“iOS开发之美”绝非哗众取宠,并不意味着其他平台就是不美。因为我并没有刻意强调iOS为最美。只是在iOS平台上做开发,确实能体验到一种编程之美。

iOS架构之美。但凡有编程思想者,在设计软件架构时,都想应用那些设计模式。在这种以UI展示为主的App开发中,更希望应用MVC模式。在我们的开发过程中,深切感受到MVC模式在iOS平台上发挥得淋漓尽致。没有哪一个手机软件平台能够比iOS的图形化编辑器更能接近真实的产品。开发一款产品原型,仅仅需要几个简单的拖拽,便跃然成型。这不能不说是iOS平台所独有的Storyboard技术成全了App开发之美。

iOS代码之美。在编写iOS代码中,我们能切实感受到,完成一项功能,只需编写短短的代码量。这奉行了“简单即是美”的原则。在斯坦福大学的iOS公开课上,我们看到的是那种行云流水般的代码编写,如同敲击钢琴键盘一样流畅,这不得不说是一种美的享受。

iOS适配之美。对App开发者来说,最担心的是那些琳琅满目的机型适配。开发一款App已经让我们筋疲力尽,如果再适配那么多的机型,让我们情何以堪。好在iOS平台已经为适配考虑得很是周全,一个Autolayout技术解决了开发者的后顾之忧。在Autolayout上做适配,如同在Word上排版一样轻松自如,无须编写大量的代码,永远是拖拽,拖拽!与其说是一种简单的操作,毋宁说是一种轻松愉悦的享受!写一本让iOS初学者看得懂的书

如同编写剧本一样,场景的顺序是多么的重要。在iOS浩瀚的知识海洋中,如何寻到一条主线,将iOS关键知识点贯穿起来,编写一本让iOS初学者看得懂的书,这是我过去几年一直努力完成的工作。

在我的网易博客上,曾有一篇博文引起了读者的强烈反响,那就是“

写给iOS初学者

”,而这篇博文的灵感,来源于自己创建的QQ群“iOS之美”。初次接触iOS开发的群友经常问起如何入手,推荐哪一本教程。iOS入门之所以让人感觉到一头雾水,是因为在过去的几年中,iOS开发平台更新得太快了,可用“频繁”两个字来形容。苹果公司是追求完美主义者的代名词,在过去的几年,iOS开发平台一直在改进,不断地在自我革命,日趋完美。

这本书没有刻意追求面面俱到,而是沿着一条主线,讲述了四个故事:Objective C编程语言、Storyboard框架、Delegate应用和TableView数据展示。(1)Objective-C语言。开发iOS App,离不开Objective-C编程语言。单要完整讲述Objective-C,就得需要大量的篇章。这里,我们着重讲述了几个关键对象的应用。例如,property、NSString、NSArray、NSDictionary等,这些都是与数据处理密切相关的概念,所有的App,都是建立在数据处理基础之上的。掌握了数据管理机制,就为数据在UI层面的展示打下了基础。(2)Storyboard技术的应用。iOS开发平台之所以强大,正是源于它拥有一个神器——Storyboard框架。试想一下,所有的App,从用户体验的角度看,不就是在多个页面之间跳来跳去么?为实现页面之间的跳转而编写大量的代码,有人愿意这么做吗?大多平台,不得已而为之。而苹果公司发布的XCode 4.2版本,通过引入Storyboard技术,成功地解决了这个问题。花大量时间,来实现页面之间的跳转逻辑,对程序员来说,这是一种简单而重复的劳动。Storyboard技术的推出,使得iOS开发者可以快速开发出一款App。在Storyboard篇,我们从多个维度讲述了Storyboard的引擎——Segue的应用。(3)Delegate的应用。Delegate是委托、代理的意思。它是一种设计模式。因为Delegate是一个抽象的概念,不容易理解。在我的博客上,曾有一篇“Delegate应用五步曲”,深受用户的点赞。为此,我重新设计了一个更加贴切的实例,并作为一道上机编程的面试题,呈现给读者。在这一章节,我们详细讲述了对Delegate的疑惑,Delegate的应用场景,其目的是为了弄清楚一个概念:难道非用Delegate不可吗?我们的回答是,在某些特定的场景下,选用Delegate就是最优的方案。(4)TableView的应用。TableView又称为表视图。表视图是iOS App中一个常用的UI对象。可以说,表视图是处理数据的神器。App中,那些可上下滑动的列表,就是表视图。iPhone手机中的通讯录,就是一个典型的表视图。表视图不仅可以显示文本,也可以显示图像,还可以内嵌按钮、输入框等。只要是涉及数据处理的App,都会用到TableView。正是因为它的强悍,TableView的应用也变得复杂起来,熟练掌握TableView需要一定的基础。这也是我们为什么把TableView放到最后一章的原因。书中的每个实例是怎样讲述的?

对于书中出现的每个知识点,都辅以相关的代码实例。每个篇章中的实例都不是独立的,而是延续从易到难的线索。拿TableView中的实例来讲,从创建最基本的工程开始,到逐步丰富这个实例的功能,中间设有六个台阶。每新增一项功能,所需添加的代码都在几行之内,以免读者一下子被大量的代码所晕倒。TableView系列教程,是我最为满意的一个章节。它涉猎了iOS的多个知识点,不管是NSArray、NSDictionary、Delegate,还是Storyboard,都在这里得到了充分的应用。

值得一提的是,在TableView实例讲解中,我们之所以将电台作为素材,是因为我对网络电台的情结之深。诞生于移动流媒体平台的“悠悠电台”,不管是过去还是现在,一直都是我们引以为豪的AppStore杰作。致谢

在本书成稿的过程中,我得到了很多人的指点和帮助,客套话不再讲太多。这里,我非常感谢曾经拜读过多遍的斯坦福大学iOS公开课。书中的部分实例,有它的影子。每次赏析公开课的视频,都能得到一次心灵的升华。

参与本书编写的还有林志红、尹陆军、袁芳、张俊、王方、马钧君、和凌群、刘晓波、王娟、张爱华。作者交流方式

作者的QQ是2385911707;作者的微信号是Leopard2385911707;QQ群(iOS之美)是238303969。和凌志2014年9月写给iOS初学者

编者按:“写给iOS初学者”是作者为iOS初学者量身定制的技术路线,深受网友好评,在此,以飨读者。

谈到iOS的学习,一下子将我和我的团队带到六年前的时光。

记得那是2007年,我们拿到了传说中的iPhone,做的第一件事情就是“越狱”。那时的iPhone,不越狱是打不了电话的。越狱的过程充满了提心吊胆,唯恐一不小心把iPhone变成“砖头”。

多少年来,我一直在手机软件行业摸爬滚打。作为一名软件工程师,自然想在iPhone上开发一款属于自己的App。终于等到2008年10月,Apple开放了SDK,自此,我们开始走向了正轨,加足马力,先后发布了几款极具商业价值的App,并打造了属于自己的移动互联网平台。

历经六年的迅猛发展,iOS开发如日中天。这一方面得益于国内广泛的iOS(iPhone/iPad)用户基础;另一方面,iOS开发平台本身,也为开发者提供了一个先天的优势。这里绝不是厚此薄彼,早在开发iOS之前,我们也曾在Nokia塞班平台上开发过软件,所经历的痛苦,可用一句话形容——“谁用谁知道”。至于Android平台,我们不想谈及太多。为对比几大平台的优劣,而引发口水战,大可不必。相信你读到这里时,对选择哪个开发平台,你已经有了自己的主见。

很多人在看了我们的App之后,经常问:你们最初是怎么学iOS的?

谈起我们初学iOS,忍不住有很多话要吐槽。这个过程依然是很艰难的。iOS开发的门槛较高,一个硬件设备,比如苹果电脑,足以把很多跃跃欲试者挡在了门外。我们也尝试过虚拟机的安装,也就是业内所说的“黑苹果”。对于“黑苹果”,我的感悟是,玩玩还行,若真的要下决心做事,还得MacBook。最初接触iOS开发时,我用过“黑苹果”。所谓“黑苹果”,就是在Windows操作系统上装一个Mac OS虚拟机。当时购买苹果电脑(MacBook),是一笔不小的开销。后来决定从事iOS开发时,最终还是痛下决心,购置了MacBook。学习iOS,常被开发环境所困扰。为装一台“黑苹果”,折腾一周算是正常,这还得运气好。在开发环境的搭建上,浪费太多的时间,得不偿失。对任何人来讲,时间才是最宝贵的财富,每个人都应该把时间投入到最有意义、最有影响的地方去。没有投入,就没有产出。如果你狠心买了MacBook,想必会坚持学好iOS,正所谓“舍不得孩子套不着狼”。

在iOS开发的旅途中,曾记录了我们多次瞬间的喜悦与感动: 当成功注册为iPhone开发会员(iPhone Developer Program,

IDP)那一瞬间; 当iPhone SDK开发环境配置成功,显现“Hello World”的那一

瞬间; 当一款自己精心设计的应用程序得以实现的那一瞬间; 当应用程序成功签名并在iPhone手机上随手指的触动而飘逸的那

一瞬间; 当应用程序成功发布到AppStore上,收到来自Apple的E-mail,

赫然标识“Ready for sale”的那一瞬间。

作为iOS初学者的你,相信在未来的开发之旅中,也会经历一些困惑与喜悦。这里,先送上一句鼓励的话:“含泪播种,必将微笑收割。”

我经常被问到的第二个问题是:iOS开发从何入手?

iOS开发没有什么神秘的地方,就开发一个App来说,这个平台已经足够强大了。iOS的版本发布虽然很频繁,但从技术框架的角度来看,iOS里程碑式的发布,主要体现在iOS5 SDK的发布,对应的XCode版本是4.2,此版本引入Storyboard技术。以我之拙见,Storyboard技术才是iOS开发框架的分水岭。至于其他版本的发布,对iPhone手机用户来讲,有多个亮点和创新,但与iOS开发者关系不大。

这里重点吹捧下Storybord技术。在我看来,Storybord技术的出鞘,让其他任何手机平台难以望其项背。试想一下,所有的App,从用户体验的角度看,不就是在多个页面之间跳来跳去么?为实现页面之间的跳转而编写大量的代码,有人愿意这么做吗?大多平台,不得已而为之。而苹果公司发布的XCode 4.2版本,通过引入Storyboard技术,成功地解决了这个问题。花大量时间,来实现页面之间的跳转逻辑,对程序员来说,这是一种简单而重复的劳动。只有工作量,缺乏技术含量。Storyboard技术的推出,使得iOS开发者能够以最快的速度,以最低的成本开发出一款App,这本身不就是强有力的竞争力么?

说了这么多,其实就为了表达一句话:学习iOS开发,一定要善用Storyboard技术。

iOS平台所涵盖的知识点浩瀚无垠,如何找到一条主线,将这些知识点贯穿起来?不妨,我们还是从App本身的特点来寻找答案吧。几乎所有的App,无非解决两个问题——UI展示和数据处理。

我们先来看看数据的展示。移动互联网应用的核心在于用户体验上,而绝妙的用户体验正是集中体现在如何流畅地展示数据。iPhone手机最为经典的数据展示页面,莫过于电话本。几千个电话号码,通过上下滑动而一览无余。为此,iOS SDK推出了表视图(TableView)技术,有效地解决了数据展示的问题。

接下来,再看看数据的交互。我们知道,每个App都是由多个页面组成的,用户在使用时,会触摸页面上的对象,页面之间也会前后跳转。其实,页面之间的跳转必将引发数据的交互。如何解决好数据的交互问题呢?iOS SDK正是借助Delegate这个设计模式,有效地解决了这一问题。

最后,又回到了UI的实现上来,正是为实现页面的跳转和页面之间的数据交互,Apple才引入了Storyboard技术。Storyboard技术才是真正意义上的王者归来。Storyboard让整个App开发的世界,变得如此简单而有趣味。

至此,或许你已经清楚了这本教程的主线,那就是Storyboard+Delegate+TableView。我们所讲述的所有实例,都是在剥茧抽丝,从多个维度来阐述这条主线的。

学iOS开发,还需要了解很多相关的知识。你需要注册Apple ID;如果想发布到AppStore,还需要注册苹果开发者账号,还要熟悉发布过程。诚然,了解和掌握这些知识点,是很重要的。但是,这些点都是对iOS开发流程相关的理解,涉及的技术知识点不多。如果将iOS周边知识点纳入到这本书中,将占大量的篇幅,就有点儿喧宾夺主了。

说一千道一万,还是那句老话,教程本身不是万能的。每个人的基础不一样,对同一个教程的领悟也不相同。我们所做的,就是帮你在iOS学习的旅途上找回信心;让你在遇到问题时,不再感到孤独无助。当你自己精心开发的App发布到AppStore的那一天,蓦然回首,你会发现,过去付出的一切,都是值得的!

第1篇 Objective-C语法篇

开发iOS App,离不开Objective-C编程语言。这里,我们着重讲述几个关键的对象的应用。例如,property、NSString、NSArray(NSMutableArray)、NSDictionary(NSMutableDictionary)等,这些都是与数据处理密切相关的概念,所有的App都是建立在数据处理基础之上的。掌握了数据管理机制,就为数据在UI层面的展示打下了基础。

第1章 iOS开发环境的搭建

子曰:“工欲善其事,必先利其器”。要想学好iOS,首先得把iOS开发环境搭建好。iOS开发环境的搭建包括硬件环境和软件环境。

1.1 硬件环境

iOS是苹果公司的产品,按照苹果公司一贯的强势做事风格,自然要求iOS只能在苹果操作系统上开发。苹果的操作系统简称为Mac OS。

如果有一台苹果电脑,那是一件很幸运的事情;当然,如果没有苹果电脑,也可以在Windows系统上装Mac OS虚拟器。这种Mac虚拟器,被称为“黑苹果”。安装Mac虚拟器,是一项艰巨的工程,也有运气的成分。你可以从网上搜到安装秘籍,这里不再赘述。

有了Mac OS后,接下来就是安装iOS开发软件了。

1.2 软件环境

仅仅有了Mac OS操作系统还不够,iOS是在特定的软件环境下开发的。这个软件就是XCode。从哪里下载XCode呢?为便于用户安装软件,苹果提供了一个软件下载平台,那就是Mac AppStore,又称之为“Mac版的应用商店”。1.2.1 注册Apple ID

为便于iOS开发者快速掌握iOS SDK开发技能,苹果公司专门提供了iOS开发中心网站:https://developer.apple.com/devcenter/ios/index.action,只要注册一个Apple ID,就能下载该网站上的技术文档。我们称这个网站为iOS Dev Center(iOS开发中心),简称IDC。IDC网站上的主要内容有: iOS SDK开发包; iOS开发文档; iOS Sample Code。

在下载这些资料之前,需要先注册一个Apple ID。小贴士:注册Apple ID是免费的,按说注册过程应该很简单才对,但有时也会遇上一点小麻烦。如果注册失败,不妨换一种浏览器试试看,比如苹果公司的Safari浏览器(注册时,电话号码的位数要保证合理)。1.2.2 XCode下载与安装

在Launchpad上,打开带有AppStore字样的图标,搜索XCode,可免费下载安装,如图1-1所示。图1-1 XCode可在Mac Appstore免费下载安装

XCode安装完成后,在LaunchPad上,会出现一个XCode标志性图标,如图1-2所示。图1-2 XCode图标

XCode是一个图形界面化开发工具。我们可以通过XCode的Storyboard,来创建App的UI布局,也可设置对象的属性。XCode统一管理代码、资源等。我们在XCode中编写代码,由XCode来编译和运行。小贴士:XCode是iOS开发必备的工具,为便于使用,可以把它放到Dock上。苹果系统下的Dock,类似于Windows系统的任务栏。单击Dock上的XCode图标,会出现一个跳动的画面,之后,在电脑屏幕的最顶端出现一个XCode工具栏。1.2.3 iOS开发语言:Objective-C

初次接触iOS的开发者,通常会问这么一句话:iOS编程语言是什么,是C、C++吗?理工科专业的学生,大都接触过C和C++,但对iOS特有的编程语言Objective-C充满了好奇。也许有人会认为,Objective-C是一门新生的编程语言。其实不然,Objective-C自1986年诞生,至今已有二十几年的发展历程。Objective-C 2.0问世,使其功能日臻完善。如同学习其他的开发平台一样,我们还是要从Objective-C编程语言开始学起。

iOS是基于Cocoa Touch开发的。那么,Cocoa Touch又是什么呢?简单地说,Cocoa Touch就是苹果公司为iOS开发提供的类库,它充分利用了面向对象的编程技术。

1.3 分享一个小故事

这里与大家分享一个小故事,目的是了解为什么Cocoa Touch类库的前缀都以NS命名。

Steve Jobs(乔布斯)是苹果公司的创始人,后来,乔布斯离开了苹果公司,创办了一个名叫NeXT Computer的新公司。NeXT公司雇佣了一些有才华的工程师组建了一个小团队,自主开发了属于NeXT的操作系统、电脑、打印机和开发工具。在当时,这些都是非常超前的。NeXT Computer后来更名为NeXT Software,操作系统和开发工具被命名为NeXTSTEP。

NeXTSTEP内置了许多库(Library)和开发工具,程序员以一种优雅的方式与窗口管理器进行交互,这些库被称为Framework。1993年,这些Framework和工具被重新修改并重命名为OpenStep,后来又被命名为Cocoa(当你接触到Cocoa类库时,你会看到所有Cocoa类的前缀都以NS命名)。

NeXTSTEP变成Mac OS X,它是UNIX的一个分支,你可以在Mac OS X上找到所有的标准UNIX程序,比如Apache Web Server,在Mac OS X上,它比在Windows更加稳定,用户界面也更加漂亮,作为一名开发人员,你将会爱上Mac OS X,因为Cocoa可以使你快捷、高效、优雅地开发出功能强大的应用程序。

许多年以来,苹果公司致力于开发一个具有NextSTEP特性的操作系统,其项目名称是Copland。后来,Copland项目出现了些问题,苹果公司最终决定放弃开发,转向从别的公司购买下一个版本的Mac OS。在经过调研当时的操作系统之后,苹果公司选择了NextSTEP,因为NeXT是小公司,1996年苹果公司直接收购了NeXT整个公司。同年,随着创始人Steve Jobs(史蒂夫·乔布斯)的强势回归,苹果公司从此开始了新的篇章。

1.4 总结与启发

相比其他App开发平台,学习iOS有一定的门槛。门槛之一就是开发设备,尽管苹果电脑也有低端的配置,但对开发者来说,有点儿小马拉大车,运行XCode有些吃力。综合性价比,MacBook Pro是一个不错的选择。

从Mac AppStore下载XCode,遇到网络不给力时,也会很闹心。如果运气好的话,从他人那里copy一个已经下载的XCode,也算是一个权宜之计,但后续的版本升级将遇到麻烦。

学习iOS另一个门槛就是传说中的Objective-C,这是一门看上去较为奇特的编程语言,说难也不难,只要弄清楚那几个“怪怪的”用法就可以了。

接下来,我们将开启Objective-C学习之旅。

第2章 Objective-C编程语言特性

2.1 Objective-C有什么奇特的地方

作为一枚“码农”,掌握几种编程语言易如探囊取物。只要接触过一门编程语言,再学习其他的编程语言,就会容易很多。因为从本质上讲,编程语言都是大同小异的,Objective-C也不例外。

所有的编程语言,尽管表达方式不同,但大多都是相通的。例如,基本的数据类型、表达式、函数的声明和调用等。这里,我们不拿Objective-C与其他编程语言对比,越是对比越让人迷惑。

我们先从字面上理解Objective-C。顾名思义,Objective-C就是一种特殊的C语言,也有人把Objective-C简称为OC。

Objective-C语言看上去怪怪的,是因为它有几个不常见的符号,例如,中括号[ ]、冒号:、加号+等。

在iOS编程中,常看到类似下面的语句:[display setTextColor: [UIColor blackColor ] ];

可以看出,这里不仅仅是一对中括号,而是多对中括号,一层套一层。

在iOS工程文件中,用得最多的文件就是.h文件和.m文件。.h文件是头文件,源自英文Header;与此对应的是.m文件,.m文件是实现文件。.m文件是Objective-C编程语言所特有的文件类型。至于为什么以.m来命名,苹果官方文档没有给出说明。既然.m文件是实现文件,据此推测,m应该是implementation(实现)的缩写。

.h和.m文件如同一对孪生兄弟,总是成对地出现。当创建一个.h文件时,同时也会创建一个同名的.m文件。也就是说,.h和.m拥有相同的文件名,但其文件类型是不同的,一个是.h类型,另一个是.m类型。

这里给出一个代码示例,初次感受下.h和.m文件的代码结构是怎样的。在XCode中,创建一个新文件:在XCode菜单栏,逐一选中File→NewFile→选择文件类型(NSObject)。这里,以创建Card文件为例,XCode会自动生成两个文件:Card.h和Card.m。

Card.h文件如下:@interface Card : NSObject@end

在面向对象的编程语言中,一个最基本的概念就是“类(Class)”。事实上,创建.h和.m的过程,就是创建一个“类”的过程。这里创建了一个Card类。“类”是具有继承关系的,NSObject也是一个“类”。Card与NSObject的关系是:Card继承了NSObject,换言之,NSObject是Card的父类。父类又称为SuperClass。关于NSObject,有一点要特别说明:iOS所有的类都继承了NSObject。通俗地讲,在iOS中,NSObject是所有类的“祖宗”。

从语法上讲,@interface与@end是一对。@interface表明类的开始,而@end表明类的结束。通过XCode创建的类,会自动生成类的结构。如果通过手动的方式自行创建的话,注意不要忘记加上@end。

接着,我们再来解读Card.m文件:@implementation Card@end

.m文件以@implementation开始,以@end结尾。乍一看,.m文件与.h文件没什么区别,其实,二者之间有一个很大的区别:.m文件中的Card没有继承NSObject。

在.m文件中,必须通过#import将对应的.h文件包含进来。这样一来,.m文件就可以引用.h文件中的@property和method了。.m文件如下:#import "Card.h"@implementation Card@end

接下来,开始为.h文件添加一个属性(@property)。代码如下:@interface Card : NSObject//声明一个属性@property (nonatomic , strong) NSString *contents;@end

如果是初次接触iOS编程,总会感到有些奇怪。声明一个变量(严格来说,是声明一个属性或实例变量),为什么还要在变量前面添加这么多的东西。“@property (nonatomic,strong)”是干什么用的呢?这就引入了Objective-C的另一个基本概念——@property的声明与使用。

2.2 如何声明实例变量(浅谈@property的使用)

关于Objective-C中的实例变量(Instance Variable),初次接触这个概念,难免会产生一些疑惑:实例变量有什么特别的呢,不就是一个变量吗?其实不然,如果仅仅是个变量的话,就不会有@property了。有了@property,其功能也就强悍了很多。@property (nonatomic , strong) NSString *contents;

在声明@property时,编译器会自动生成两个方法——getter和setter。顾名思义,getter是用来获取实例变量的值,而setter是用来赋值的。通过重写getter和setter,简单的几行代码就能实现看似复杂的逻辑。

对于@property的理解,只有通过具体的应用场景,才能做到灵活掌握,这是一个“悟”的过程。在后续章节中,我们会从多个维度来讲解getter和setter的妙用。

Nonatomic是一个与线程安全(Thread Safe)机制相关的概念,理解起来,未免有些抽象。当声明@property时,通常会用到nonatomic。

NSString是一个对象数据类型,所有的对象数据都是存放在堆(Heap)中,对象都是通过指针来声明的。当要声明一个NSString类型的实例变量时,只能用NSString*,绝不可以只用NSString。带不带*,有着本质的区别。

讲到这里,顺便提及下:数据类型分为Object Data和Primitive Data.在Objective-C中,Primitive Data主要有int、float、BOOL。

Object Data和Primitive Data的区别在于,前者用strong,后者不用strong;前者带*,后者不带*。举例来说:@property (nonatomic,strong) NSString *contents;@property (nonatomic) BOOL onSwitch;

在声明一个@property时,可以在.h文件中声明,也可以在.m文件中声明。在不同的文件中声明,它的影响范围是不同的。在.h文件声明的@property,是公有(public)的;而在.m文件声明的,是私有(Priviate)的。具体来说,在.h声明的@property,可以在其他文件中调用,而在.m中声明的@property,只能在该.m文件中使用。

如果是初次接触iOS开发,感觉不到这种声明有什么区别。但如果要开发第三方控件或framework什么的,对这个声明需要有一个清晰的理解。作为被其他文件调用的@property,其实就是API。哪些想让别人调用,哪些不想让别人调用,甚至根本不想让别人看见,这就得好好设计下了。提供的第三方framework,需要尽可能少地暴露@property和method。把让人家用的,暴露出来;用不着的,暴露出来反倒让人迷惑。这个时候,一定要想好,哪些@property和method放在.h文件中,哪些放在.m文件中。

2.3 NSString的应用

在任何一门编程语言中,对字符串的处理都是很重要的一点。在Objective-C中,字符串是指NSString对象,NSString有多个属性和方法,这里只介绍最常用的几个。

NSString的赋值方法如下:(1)直接赋值,通常用于常量字符串的赋值。例如:NSString *myString = @”hello”;(2)多个字符串的拼接。例如:NSString *myString =[otherString stringByAppendingString:secondString];(3)创建格式化字符串。通过这种方式,可以将整型转换为字符型。NSString *myString =[NSString stringWithFormat:@“%d%@”, myInt, myObj];

2.4 如何判断两个字符串是否相等

在iOS开发中,有时需要判断两个字符串是否相等。对于初学者来说,由于概念不清楚,经常出现一些诡异的错误。

这里给出代码示例:NSString * strA = @”abc”;NSString * strB = @”abc”;if( strA == strB)NSLog(@"A is equal to B");elseNSLog(@"A is not equal to B");

运行这段代码,发现输出的结果是:A is not equal to B

想必你会产生一些疑问,这是为什么呢?这两个字符串明明是相等的嘛。问题出在字符串对比的方法有问题。

深度分析下这个条件判断语句吧。“if(strA == strB)”中的strA和strB分别对应一个指针。虽然字符串的内容是相同的,但指向字符串的指针肯定是不同的,而且也不能相同。我们的本意是想比较两个字符串是否相等,而这条语句比较的是两个指针是否相等。读到这里,你或许会豁然开朗,原来如此啊!

那该怎么办呢?是不是需要特意为此编写一个方法呢?当然大可不必。iOS已经为此提供了一个简洁实用的方法,这就是“isEqualToString:”方法。

将字符串判断语句改为if([strA isEqualToString:strB])

再运行一次,你会发现输出的结果是A is equal to B

这个结果,也正是我们所期待的。

在使用“isEqualToString:”方法时,需特别注意:if的后面是逻辑判断,所以必须用一对括号“( )”,而不是中括号“[ ]”;而“isEqualToString:”是方法调用,所以必须用中括号“[ ]”。我们知道,对method的调用,是通过中括号“[ ]”来完成的。

需要指出的是,在iOS开发中,经常出现括号“( )”和中括号“[ ]”,二者是有差别的。

2.5 对象类型与基本数据类型的混合使用

在Objective-C中,有两种数据类型:对象类型和基本数据类型。对象类型就是Object Data,而基本数据类型就是Primitive Data。二者区别又是怎样的呢?

从字面意义上讲,Primitive Data就是基本数据类型,又称之为原生数据类型或简单数据类型,具体区别如下。

1.基本数据类型(Primitive Data Type)(1)数据类型(Data Type):仅仅是一个数据而已,可以直接使用、直接赋值。(2)常用的数据类型有int、float、long、Boolean。需注意的是:NSInteger也是数据类型。(3)Primitive Data用不着指针,看不到*。(4)以NS打头的数据类型,不一定都是对象。例如,NSInteger就属于Primitive Data,而NSNumber却是对象,所以说NSNumber的操作比NSInteger要复杂得多,当然NSNumber功能也更强大。

2.对象类型(Object Type)(1)对象类型(Object Type):所对应的是一个对象,对象强大,它所包含的不仅仅是属性,还包含方法(method)。(2)Object = Data + method。通常一个对象中,既有属性也有方法,所以无法直接给一个对象赋值。例如,给UITextField对象赋值的方法是:textField.text = @"example";

而不能是:textField=@“example”;(3)声明一个对象,类似@property(nonatomic, strong)NSArray * foo;

需要特别注意的是:这个*是必不可少的。(4)对象在使用时,一定要做初始化。

仅仅知道Primitive Data和Object Data的区别还是远远不够的,在数据处理和数据存储时,常常用到NSArray和NSDictionary。NSArray和NSDictionary可以理解为是两个容器,这两个容器所装载的内容都是对象格NSDictionary可以理解为是两个容器,这两个容器所装载的内容都是对象格式。

Primitive Data是一个基本数据类型而不是对象类型,这就意味着Primitive Data不能直接存放到NSArray或NSDictionary中。但很多时候,需要将Primitive Data装载到NSArray和NSDictionary中,遇到这种情况,该怎么办呢?

2.6 对象类型与基本数据类型的转换(NSNumber与NSInteger)

这里以NSNumber和NSInteger为例,NSNumber是对象类型,而NSInteger是基本数据类型。

NSNumber所拥有的类方法,如下:+(NSNumber*)numberWithChar: (char)value;+(NSNumber*)numberWithInt: (int)value;+(NSNumber*)numberWithFloat: (float)value;+(NSNumber*)numberWithBool: (BOOL)value;

NSNumber通过以上类方法,就可以完成一个基本数据类型到对象数据类型的转换。

作为一个类,NSNumber经常与*一起使用。例如:NSNumber * intNumber = [NSNumber numberWithInt:100];

将基本类型封装到NSNumber中后,就可以通过方法的调用重新获取它。这里以整型为例,通过NSNumber的实例方法“-(int)intValue”可以获取NSNumber的整型值。myInt = [intNumber intValue]; //获取对象的整型值

为强化NSNumber和NSInteger的概念,这里给出一段代码,从编译的角度看看有什么问题呢?NSMutableArray *myMutableArray =[[NSMutableArray alloc] init];[myMutableArray addObject: 100 ];

这样是会引发编译错误的,因为NSMutableArray只能存放对象类型,但“100”不是对象,而是一个基本的int型。为解决这个问题,需要用到NSNumber。改动如下:NSMutableArray * myMutableArray =[[NSMutableArray alloc] init ];[myMutableArray addObject: [NSNumber numberWithInt:100] ];

经过改动后,基本类型(int)的数据成功转型为对象类型的数据了。

NSArray和NSDictionary是两个强大的数据容器,可以容纳各种类型的对象数据,却无法容纳基本数据类型。

2.7 不可变数组(NSArray)与可变数组(NSMutableArray)

在iOS开发中,有时需要把多个对象存放在一个容器中,然后对这个容器统一处理,这时候,就引入了数组和字典的概念。

在Objective-C中,数据的管理通常是通过数组和字典来完成的。与其他编程语言不同的是,Objective-C的数组和字典的功能非常之强大。它已经不再是一个简单的数组,而是一个庞大的对象容器了。

数组分为不可变数组和可变数组。NSArray是不可变数组,与此相对应的是可变数组(NSMutableArray)。换个说法,NSArray是静态数组,而NSMutalbeArray是动态数组。2.7.1 不可变数组(NSArray)的特征

NSArray是一个数组,它是一个有序的对象集合。也就是说,NSArray所加载的每个元素都是对象。

NSArray是一个不可变数组。一旦创建了一个NSArray,并填充了对象,在程序运行过程中,是无法再为数组增加一个对象的,删除一个对象也不行。

为便于对数组操作,NSArray提供了以下主要的方法。//用来计算数组中对象的个数-(NSUInteger)count;//获取数组中指定下标的对象-(id)objectAtIndex:(NSUInteger)index;//获取数组中的第一个对象-(id)firstObject;//获取数组中的最后一个对象-(id)lastObject;2.7.2 可变数组(NSMutableArray)的特征

可变数组是不可变数组的延伸,NSMutableArray 是NSArray的子类。这就是说,不可变数组所具有的特征,可变数组都具备。不可变数组不能添加或删除对象,而可变数组是可以的。

创建一个可变数组对象的方法如下:NSMutableArray * myMutableArray=[[NSMutableArray alloc] init]

NSMutableArray继承了NSArray 的所有方法,除此之外,可变数组(NSMutableArray)还有以下几个特有的方法。//在可变数组的末端添加一个对象-(void)addObject:(id)object;//在可变数组指定的索引位置添加一个对象-(void)insertObject:(id)object atIndex:(NSUInteger)index;//在可变数组指定的索引位置,删除一个对象-(void)removeObjectAtIndex:(NSUInteger)index;2.7.3 如何遍历数组中的对象

当我们定义了一个数组,并对该数组进行了初始化后,接下来要做的是,如何获取到数组中的对象,并对数组中的对象进行处理。这就要解决一个问题,如何遍历数组中的对象?

按照习惯性的编程思路,做法是先计算出这个数组有多少个对象,再通过for循环语句来获取数组中的下标。代码示例如下:NSArray *myArray=@[@"a",@"b",@"c"];//先计算数组中有多少个对象,通过NSArray的count方法for(int i=0; i< [myArray count]; i++){//根据数组的下标,获取到数组的对象NSLog(@"for loop: %@",myArray[i]);}

这种方法虽然可以遍历数组中的对象,但效率并不是很高。为了更加高效地遍历NSArray中的对象,Objective-C提供了特有的for-in语句。(1)假定我们事先知道数组中的对象都是NSString类型,在这种情况下,指定对象的类型,进行遍历。代码示例如下:for(NSString * string in myArray){NSLog(@"NSString: %@",string);}

在这种指定类型的情况下,需要注意一点:编译器并不知道数组中存有哪些类型的对象。for(NSString * string in myArray){//如果数组中不完全是NSString 类型的对象,在这种情况下,//程序将出现crash(闪退)int value =[string integerValue];}(2)数组是一个对象容器,可以存放不同类型的对象。假定数组中存有多个不同类型的对象,又该如何处理呢?

在这种情况下,将采用一种更为通用的方法。先将NSArray中的对象类型统统视为ID类型,在对ID进行操作时,再判断对象的具体类型。for(id obj in myArray){//判断对象的类型,只有满足条件,才做处理if([obj isKindOfClass:[NSString class]]){//对NSString类型的对象,进行方法调用int value =[obj integerValue];}}

条件判断语句if([obj isKindOfClass:[NSString class]])

是必不可少的,只有判断它是NSString类型的对象,才能调用NSString所拥有的方法,否则会发生闪退(crash)。

2.8 不可变字典(NSDictionary)与可变字典(NSMutableDictionary)的应用

2.8.1 不可变字典(NSDictionary)

与数组(NSArray)对应的是字典(NSDictionary),字典也分为不可变字典(NSDictionary)与可变字典(NSMutableDictionary)。

字典也是一个数据容器,字典所加载的数据也都是对象类型。但与数组不同的是,字典里面的数据是以Key-Value格式呈现的。

Key-Value又称为键值对。这就是说,字典中所存储的数据是一对一对出现的,根据Key值,得到对应的Value。这个Value也可以理解为对象,所以又称为Key-Object。

创建NSDictionary对象的语法是:NSDictionary * dic = @{ key1:value1, key2:value2,key3:value3 };

例如,我们要创建一个字典,字典的Key是颜色名称,Value是颜色的值。NSDictionary *colors =@{@"green": [UIColor greenColor], //green的Key-Vaule@"blue": [UIColor blueColor], //blue的Key-Vaule@"red": [UIColor redColor] //red的Key-Vaule};

也许你已经注意到了,Key就是一个字符串。字典中的Key必须是唯一的,不能重复。这是因为,我们要根据Key标识符来获取到该Key所对应的Value。

NSDictionary中的方法“-(id)objectForKey:(id)key;”就是用来获取字典中的对象的。

假设我们要获取字典中的红色值,需要根据Key的@“red”,获取到对应的Value,有以下两种方法。

第一种方法:NSString *colorKey =@"red";UIColor *colorObject = colors[colorKey];

第二种方法:UIColor *colorObject = [colors objectForKey:@"red"];

既然有两种方法,那么用哪一种方法好呢?二者没有孰优孰劣,这取决于个人偏好。通常来讲,第二种方法的代码可读性更强些,因为它更为直观地表达Key-Value键值对的概念,通过Key,来获取到该Key对应的Value,一目了然。2.8.2 可变字典(NSMutableDictionary)

关于不可变数组(NSArray)与可变数组(NSMutableArray)的区别,前面已经有所讲述。有了这个基础,可以很容易理解不可变字典(NSDictionary)与可变字典(NSMutableDictionary)的差异在什么地方。

NSMutableDictionary继承了NSDictionary,所以说,NSDictionary所拥有的方法,NSMutableDicitonary都有。除此之外,NSMutableDicitonary还独有以下方法。//添加一对Key-Object,-(void)setObject:(id)anObject forKey:(id)key;//删除一对Key-Object-(void)removeObjectForKey:(id)key;//删除字典内所有的Object-(void)removeAllObjects;2.8.3 如何遍历字典中的对象

我们知道,字典中的对象都是以键值对(Key-Value)的形式呈现的,而且,Key又都是唯一的,既然这样,就可以通过遍历Key来获取到对应的Value。所谓遍历,就是按照一定的规则,将字典中所有的Key都检查一遍。

NSDictionary中的数据存储结构,从本质上讲,就是一张哈希表(Hash),根据Key值,直接访问数据结构。哈希表是一种非常有用的数据结构,哈希表所带来的查找和插入效率,都是让人十分满意的。

Objective-C中的数据字典(NSDictionary),就是很好地利用了哈希表的特性,通过Key,可以快速地找到该Key对应的Value。在Objective-C中,这种Key-Value,也称为键值对。在XCode中,应用十分广泛。例如,info.plist文件结构,就是典型的Key-Value数据结构。

与NSArray类似,通过Objective-C特有的for-in语句,可以很方便地实现NSDictionary的遍历。代码示例如下:NSDictionary *myDictionary =……;for(id key in myDictionary){//通过Key,获取到对应的Valueid value = [myDictionary objectForKey:key];//接下来,就可以对Value进行操作了}

2.9 一种最简单的永久数据存储方式(NSUserDefaults的应用)

1.数据存储的两种方式

在一个应用程序中,数据存储的方式有两种:(1)程序运行时的数据存储。程序在运行时,将数据存放在NSArray、NSDictionary中,一旦程序退出,这些数据将不复存在,再打开程序时,将重新填充这些数据。(2)永久性数据存储。所谓永久性,是指当应用程序退出再打开,这些数据仍然是存在地的。永久存储的应用场景是:用户设置了一些个人偏好,希望程序再次打开后,仍然保持退出前的样子。

在iOS中,永久性数据存储的方式,总结起来,有五种: User Defaults; Property List; Object archives; SQLite 3; Core Data。

这五种方式,一个比一个复杂,一个比一个功能强大。最简单的永久性数据存储方式是NSUserDefaults。

NSUserDefaults的数据存储格式,仍然是Key-Value格式。由此可见,Key-Object在iOS开发中使用得是多么广泛。只要理解了Key-Value(或Key-Object),对于的数据的管理,就可以做到驾轻就熟了。

2.NSUserDefaults使用三部曲

第一步:通过NSUserDefaults的类方法获取到应用程序的共享实例(Shared Instance)。例如:NSUserDefaults * myUserDefaults =[NSUserDefaults standardUserDefaults];

第二步:指定一个唯一的Key,并对该Key设定对象值。[myUserDefaults setArray: myArray forKey:@“RecentlyViewed”];

其前提条件是:myArray是一个NSArray实例,并且已经进行了数据的初始化。

为便于NSUserDefaults对数据的操作,它还提供了以下几种方法:-(void)setDouble:(double)aDouble forKey:(NSString *)key;-(NSInteger)integerForKey:(NSString *)key;-(void)setObject:(id)obj forKey:(NSString *)key;

其中,最常用的方法是-(void)setObject:(id)obj forKey:(NSString *)key;

在使用这个方法时,所要存储的Object是有一定格式要求的,必须是propertylist属性的类型,这些类型有:NSArray、NSDictionary、NSNumber、NSString、NSDate、NSData及其他们对应的可变类型(Mutable Class)。

第三步:同步。

通过“setObject:(id)obj forKey:”这个方法设置的数据,还没有永久存储到手机的本地文件中。只要应用程序重新启动,这个对象就变为空了。为了确保数据的持久化,需要通过NSUserDefaults的synchronize方法。[[NSUserDefaults standardUserDefaults] synchronize];

需要提示的是,初次接触iOS的开发者,一不小心就会遗漏这个步骤,到头来觉得莫名其妙,明明是设置了Object,应用程序重新启动后,反倒变成空的了。原因就是疏忽了同步这一步。

3.NSUserDefaults的特点

NSUserDefaults虽然可以永久性数据存储,但它可存储的数据量是有限的,属于轻量级存储。具体的数据量,苹果的官方文档没有给出说明,通常存储几KB的数据是没有问题的。

2.10 创建类的对象的两种方法

在使用对象之前,我们要先创建一个对象。创建一个对象的方法有两种。(1)最常用的方法是通过alloc和init来创建对象。例如:NSArray *cards = [[NSArray alloc] init];(2)通过类方法来创建对象,NSString常用这种方法。例如:NSString * count = [NSString stringWithFormat:@“%d”, 100];

如果声明了一个类,在使用这个类之前,必须要创建这个类的对象。创建的方法如下:MyClass * myObj = [ [MyClass alloc] init ];

这里,要注意中括号的使用,[ ]也是可以嵌套的。因为alloc和init都是方法(Method),所以只支持[ ]操作。

2.11 一种最简单的调试方法(NSLog的使用)

在iOS开发中,总会遇到这样或那样的一些问题,解决的方法就是Debug或输出Log。

你可以很方便地在模拟器上调试、验证你想要的结果。如果你想跟踪某个对象的内容,可以通过设置断点的方式来查看,另一种极为简单的方法就是通过NSLog( )来查看调试信息。

Objective-C中的NSLog( )类似于C语言中的printf( )。NSLog可以拿来直接使用,不用纠结于它是类方法还是实例方法。NSLog的使用方法如下。

直接输出一个字符串:NSLog (@"this is a Debug");

输出某个基本类型变量的值:NSLog (@"The middle of value is : x=%d ", intValue);

NSLog有一个更为强大的功能,就是可以直接输出对象的内容。例如:NSLog(@”The Object is: %@”, myObject) ;

2.12 忍不住也来说说iOS的设计模式(MVC的使用)

不管是什么软件开发平台,都会宣扬其设计理念,大谈特谈其设计模式。这里也忍不住想说说iOS的设计模式。

iOS最为基本的设计模式,就是MVC。

大多MVC参考书都是这样介绍的:MVC是一种设计模式,M:Modal、V:View、C:Controller。Controller控制视图的显示,Modal提供数据。Modal与Controller交互,但Modal从不与View直接打交道。MVC解决了视图与数据隔离的问题,降低了数据与视图的耦合性,便于维护。

对于iOS初学者来说,看完这段话,是没有感觉的。当我们学习某种模式时,首先要明白它讲的是什么?为什么用它?怎么用它?明白了这三点,那才是真正得明白了。

MVC的概念,我们已经清楚了。不清楚的是:为什么要用MVC?仅仅讲到数据与视图的分离,这是不接地气的。也就是说,大道理都明白。谈到具体的使用,模棱两可。

如果不想再单独创建一个Modal文件,直接把数据放在Controller中,难道这样不行吗?这还真的行。我们注意到,确实有些App就是这样做的。当App数据量很小时,体现不出设计模式的优势。给人的感觉是,想怎么用就怎么用,反正功能也实现了,而且,还减少了Modal文件,感觉更容易些。

这里,我们所讨论的针对较为复杂的App。对于复杂的App来说,首要特征就是数据量庞大。数据的管理是一个难点(这里暂不讨论数据管理问题,后续章节有单独的介绍)。

做软件,总是希望能复用。对于App来说,复用有以下几种情况。(1)数据的复用:如果新创建了一个project,但想重用之前App的数据。也就是说,换汤不换药。同一套数据,不同的View。这个时候,就需要把之前的数据分离出来了,分离到一个独立的数据文件中。在新的project中,直接加载这个数据文件就可以了(这里说的数据文件,不是某个plist、sqlite文件,而是Modal下具有逻辑功能的文件)。(2)视图的复用:同一套UI框架,只是展示的数据不同。这个时候,数据文件、视图(View)文件和视图控制器(View Controller)文件是完全独立的。(3)Universal App:这种情况最为常见。一个App,既支持iPhone版,又支持iPad,称之为Universal版。既然属于一个App中,就应该共享同一数据。如果数据不是独立的模块,而是将数据混在了View Controller中,那么管理起来将非常不方便。难怪有iOS开发者抱怨,做一个Universal App还不如单独开发两个App简单:一个iPhone版App,一个iPad版App。如果真的是对同一个产品创建两个工程的话,后期维护的工作量可想而知。

2.13 总结与启发

在iOS开发中,离不开数组(NSArray)和字典(NSDictionary)。这个数组不是传统语言的数组,它只能存储对象,而不能存储简单数据类型(如int、char等)。数组也好,字典也罢,它们都是一个对象,而且也都可以存储多个对象的容器。既然如此,在数组中,就可以套字典;在字典中,也可以套数组。

这里给出一个应用场景。例如,一个TableView是由多个cell组成的,为创建一个高效的数据结构,用数组最合适不过了。TableView的行,正好对应数组的下标;而每个cell又由多个对象组成(如缩略图、主标题、副标题等),将每个cell对应的数据存成一个字典,是一个不错的数据存储结构。这样一来,“数组套字典”对TableView的实现来说,再合适不过了。

而常用的plist文件,是一个典型的字典数据结构。字典是Key-Object结构,而这里的Object又是以数组方式存在的。这就是“字典套数组”结构。

通俗地讲,再复杂的数据结构,无非就是“数组中套着字典,字典中套着数组”。

学习任何一门编程语言,如果仅仅是钻研语法,未免有些枯燥。一种更为实用的学习方法是:“在练中学,在学中练”。我们之所以匆匆忙忙地讲完了Objective-C,目的是从容不迫地讲述iOS编程实例。对每个实例,我们通过短短的几行代码,来形成一个基础的知识点。希望读者在阅读这些代码时,是一个享受的过程。这里讲述的每个实例,都不是独立的,而是一个连续的过程。如同爬山,拾级而上,直至登峰。

第2篇 Storyboard技术篇

iOS开发平台之所以强大,正是源于它拥有一个神器——Storyboard框架。试想一下,所有的App,从用户体验的角度看,不就是在多个页面之间跳来跳去么?苹果公司发布的XCode 4.2版本,通过引入Storyboard技术,成功地解决了场景跳转问题,从而使得iOS开发者可以快速开发出一款App。这里,我们将从多个维度讲述Storyboard的引擎——Segue的应用

第3章 iOS常用开发控件

在讲述iOS常用开发控件之前,我们先来了解下iOS开发的两大阵营:基于XIB的App开发与基于Storyboard的App开发。

3.1 XIB App与Storyboard App之争

掐指一算,苹果推出Storyboard框架已经有几个年头了,那是在iOS5版本发布时,苹果首次推出了Storyboard技术。从字面上理解,Storyboard是“故事版”的意思。Storyboard的推出,对iOS开发界,是一个不小的震撼。对于Storyboard框架,积极推崇者有之,质疑者有之。自然而然地,在iOS开发者队伍中,形成了两大阵营——XIB App与Storyboard App。

多少年过去了,Storyboard使用者与日俱增,之前的争议之声,渐渐地销声匿迹了。XIB Apps代表了iOS的过去,而Storyboard Apps代表了iOS的现在和未来。也正是在这个技术背景下,本书讲解的所有实例都是基于Storyboard框架开发的。在开始编码之前,我们先熟悉下Storyboard的开发环境。

3.2 Storyboard开发环境

首先,需要配置好iOS开发环境。如果还没安装好XCode,请参考本书的起始章节。

几乎所有的编程语言参考书,都是从Hello World讲起的。这是初学者创建第一个应用程序的惯例,这里,我们将继续延续这一习惯。

通过一个最简单的工程创建,可以学习如何编译、运行应用程序。更为重要的是,通过这个Hello World的学习,你会发现,iOS开发没有想象的那么难,从而更加坚定你学习iOS的信心。

整个书稿中,只有这一个实例是通过step by step以图文方式讲解的。其他的示例,更多的是强化编程的思想,淡化操作的步骤。

这里,我们仅创建一个最简单的工程,并不编写一行代码,程序运行的结果也只是一个白屏,以此为起点,开启你的iOS编程之旅。

3.3 创建一个新的工程

启动XCode:如果是从Mac AppStore下载并安装的XCode,你可以在LaunchPad中找到并双击XCode图标,进入XCode编辑页面,如图3-1所示,单击XCode菜单栏,逐层进入:File→New→Project…。图3-1 XCode界面

XCode给出一个模板选择窗口,有多个模板供选择。对于一个没有特别之处的App,通常选择“Single View Application”模板,单击“Next”按钮,进入下一步。

接下来,弹出另外一个窗口,需要为创建工程而填写一些必要的信息。如图3-2所示。我们逐个介绍下这些填写项。图3-2 创建工程的选项

你可以简单输入以下选项:(1)Product Name:HelloWorld,应用程序的名称。(2)Organization Name:无关紧要,自行填写。(3)Company Identifier:这个域名看上去有些奇怪,它是反向输入的;如果你有自己的域名,最好使用自己的。(4)Class Prefix:这是一个类前缀。XCode会自动使用这个名称作为类的前缀。当然,在后续的编码过程中,也可以修改这个前缀名称。可以先不管它。(5)Device:iPhone,该App以iPhone作为目标设备。如果是开发iPad App,这里就要选择iPad了。

单击“Next”按钮继续,XCode接着问在哪里保存HelloWorld工程。可以选择Mac下的任何目录。之后,XCode会自动创建一个HelloWorld工程。

3.4 熟悉XCode开发环境

工程创建后,将出现一个编辑界面,如图3-3所示。不管是视图间的跳转,还是代码的编写,都是在这个界面中完成的。编辑界面中的每个点选操作,都会有不同的响应。在XCode上多操作几下,就会逐步熟悉起来。图3-3 XCode编辑界面(1)左侧区域:工程导航栏。你可以在这里管理工程的文件,单击哪个文件,就显示该文件的内容。(2)中间区域:编辑区域。你可以在这个区域对工程进行配置,也可以编写代码。这个编辑区域的展示,取决于你在工程栏中所选择的文件。单击不同的文件,该区域所显示的内容和格式都不同。尤其是单击Main.storyboard文件时,它所呈现的是App的UI界面。所有的UI层面的东西,都是在Main.stroybard中完成的。(3)右侧区域:工具区域(Utility Area)。该区域的下半部分是对象库(Object Library),如果你想添加一个控件,从这个对象库中找到这个控件,并拖拽到Main.stoyboard的视图中,就可以了。

如果想知道每个图标所表达的意义,有一个小技巧:将鼠标停留在这个图标上,多停留几秒,就会自动弹出提示信息。

3.5 运行HelloWorld

运行下这个HelloWorld吧。单击“Run”按钮(或快捷键“Comand+R”)。运行结果是一个白屏,如图3-4所示。图3-4 HelloWord运行结果只是一个白屏

选择此时的菜单栏Hardware→Home(或快捷键Shift+Command+h),App退出,返回到主屏幕。

在iPhone的主屏页面,会出现一个白色的图标,并标识HelloWorld,这就是我们刚才创建的最为简单的App。

基于Storyboard创建HelloWorld非常简单,用不着编写一行代码。通过这个简单的应用程序,我们可以初步了解XCode编程环境,如何使用Storyboard创建用户界面。这个HelloWorld工程,仅仅是一个白屏显示,因为我们还没有用到任何控件。要想开发出功能强大的App,就得熟练掌握iOS常用的控件。这就是我们接下来要介绍的内容。

3.6 按钮(UIButton)与标签(UILabel)

iOS开发是在XCode上进行的,而XCode是一个图形化界面开发平台。所谓图形化,就是所见即所得,App常用的控件都可以在XCode的objectlibrary中找到。如果对每个控件都展开讲一遍,这得需要太多太多的篇幅。其实,大可不必。每个控件都是一个对象,而对象都会拥有属性和方法。我们只要弄清楚每个控件的属性和方法就可以了,其用法大同小异。

在iOS App中,最常用的控件莫过于按钮(UIButton)和标签(UILabel)的使用。这里给出一个使用场景,再给出相关的代码。

场景描述:在UIViewController上,拖放一个按钮(UIButton)和一个标签(UILabel),当单击按钮时,标签上显示“这个按钮被选中!”。

App所展现的效果如图3-5所示。接下来,我们一步步演示实现的步骤。图3-5 单击Button后,Label有相应的显示3.6.1 创建UIButton的IBAction

在Storyboard中,双击“ViewController”,使其呈蓝色边框;选中Button,通过“Ctrl+drag”操作,向对应的ViewController.h或ViewController.m文件拖拽,出现一条蓝线,如图3-6所示。图3-6 创建Button的Target-Action

松开鼠标,此时弹出一个窗口,如图3-7所示,注意窗口中的几个选项。图3-7 创建Button后弹出的窗口(1)Connection:选中Action。因为我们要创建Button的IBAction,如果是创建Button的IBOutlet,则选择Connection中的Outlet。(2)Event:选中Touch Up Inside。这表明,当单击这个按钮并松手之时,将触发这个事件。小贴士:什么是“Ctrl+drag”操作?XCode是一个可视化集成编程。在Storyboard编辑页面中,经常需要一些拖拖拽拽的操作。如果仅仅是改变控件的大小和位置,只需要选中这个控件,就可以完成操作。但在Storyboard中,有一种更为强大的拖拽功能,那就是“Ctrl+drag”操作。所谓“Ctrl+drag”操作,是指:左手按住Control键不放,右手用鼠标选中某个对象,向目标位置拖拽。当达到目标区域时,会弹出一个选择框。至于这个选择框具体的内容,它取决于具体的操作场景。拖拽的目标位置取决于你要做什么。如果想要声明对象的属性或方法,目标就是该对象所对应的.h或.m文件;如果要指派一个委托,拖拽的目标位置就是Scene中的ViewController;如果要创建一个Segue,拖拽的目标就是指向另外一个Scene。这几种拖拽,我们在示例中都有相关的应用。3.6.2 创建UILabel的IBOutlet

采用同样的方法,创建UILabel的IBOutlet。选中UILabel对象,通过“Ctrl+drag”操作,弹出如图3-8所示的窗口。注意,这次Connection选中的是Outlet。图3-8 创建Label的Outlet

经过以上操作后,在ViewController.h文件中生成的代码如下:@interface ViewController : UIViewController-(IBAction)buttonClicked:(UIButton *)sender;@property(weak, nonatomic) IBOutlet UILabel *textLabel;@end3.6.3 实现Button被触发后的方法

在ViewController.m文件中,实现Button被单击后的方法。代码如下:-(IBAction)buttonClicked:(UIButton *)sender{//设置UILabel所显示的内容self.textLabel.text=@"按钮被单击!";}

只要添加了一个IBOutlet或IBAction,在它的代码行的左侧,就会出现一个小圆圈,当鼠标单击这个小圆圈时,左侧对应的ViewController中的控件就会跟着联动,如图3-9所示。图3-9 对象与对象的Target-Action是一个联动的关系

通过“Command+R”快捷键,运行下,看看效果吧。如果没有意外,应该能达到我们所期望得那样。

3.7 如何删除对象的IBOutlet或IBAction

如果已经声明了一个对象的IBOutlet或IBAction,现在又不想要了,那该怎么办?如果很生硬地删除这个IBOutlet或IBAction代码,编译时会报错,而且看上去是一个极其诡异的错误。类似:Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key lblTest.

那么,如何删除一个已创建好的IBOutlet或IBAction呢?可通过以下三个步骤完成。(1)在Storyboard中,用鼠标右键选中这个对象,会弹出一个黑色的窗口,这个窗口显示了该对象所有的IBOutlet和IBAction的关联状态(这是一个深黑色的窗口,考虑到印刷效果,这里不再给出示意图)。(2)删除不想要的关联。(3)再删除与这个关联相关的代码。

3.8 让键盘消失的四种方法

在iOS App中,只要用到编辑框(UITextField)的地方,就得处理键盘消失(Dismiss Keyboard)问题。

创建一个新的工程,并在Storyboard中,添加两个对象:UILabel和UITextField。UILabel仅仅是一个标签,用来给出一个标示,例如,“输入内容”。输入完成后,单击键盘的“return”按钮,键盘就会被隐藏。运行结果如图3-10所示。图3-10 单击键盘的“return”按钮,键盘消失

我们先来了解下UITextField这个控件。UITextField是一个输入框,键盘的出现是有条件的。 当UITextField处于first responder状态时,键盘会出现。 当用户单击UITextField时,键盘会出现。

如果想让键盘出现,只需要给UITextField发送一个消息becomeFirstResponder即可。例如:[UITextField becomeFirstResponder];

我们可以让键盘有条件地出现,也可以让键盘有条件地消失。这样一来,就可以随心所欲地控制键盘何时出现,何时消失。与becomeFirstResponder对应的方法是resignFirstResponder,如果想让键盘消失,只需向UITextField发送resignFirstResponder消息即可。代码示例:[UITextField resignFirstResponder];

接下来,我们来介绍下键盘消失的应用场景: 当用户输入完成,单击键盘的“完成”按钮时,键盘需要消失。 当用户单击输入框以外的区域时,键盘需要消失。

在第一种情况,当用户单击键盘的“完成”按钮时,会触发UITextField的Delegate方法。-(BOOL)textFieldShouldReturn:(UITextField *)sender;

只要在这个方法中,调用“[sender resignFirstResponder];”就可以让键盘消失。

以上讲述了键盘的出现和消失机制,接下来,我们一步步演示如何让键盘消失。3.8.1 键盘消失方法一:Delegate应用三步法

简单来说,让键盘消失需要三个步骤。 遵循:遵循代理协议,ViewController要遵循

协议。 调用:调用代理的方法,在“textFieldShouldReturn:”中调用

resignFirstResponder。 关联:关联委托者与被委托者,将UITextField对象的Delegate指

派给该对象所在的ViewController。

接下来,我们通过一个实例工程来演示操作的步骤。基于Single View Template创建一个工程,单击Main.storyboard文件,从Object Library选中UITextFiled控件,拖拽到视图控制器中。

第一步:遵循协议。在ViewController.h文件中,添加以下代码:#import @interface ViewController :UIViewController @end

第二步:释放第一响应者resignFirstResponder。在ViewController.m文件中,添加以下代码:#pragma mark —— uiTextFieldDelegate-(BOOL) textFieldShouldReturn:(UITextField *)textField{[textField resignFirstResponder];return YES;}

完成以上两步后,我们试着运行下这个工程,查看运行结果。单击输入框后,会弹出键盘,随便输入几个文字。此时,单击键盘的“完成”按钮,你会发现,键盘并没有消失。

也许,你会怀疑代码有问题,那该如何排查呢?当然,可以通过Debug的方式,设置断点来排查;另外,还有一种更为简单的方法,通过NSLog来排查。添加一行NSLog代码。如下:-(BOOL) textFieldShouldReturn:(UITextField *)textField{//用来验证程序是否执行到这里,如果执行到这里,//将输出一行文字:"textFieldShouldReturn:reached!"。NSLog(@"textFieldShouldReturn: reached!");[textField resignFirstResponder];return YES;}

再次运行,执行同样的操作。在Log输出窗口中,并没有出现这行Log信息。这说明,这行代码没有执行。原因何在?

对于一个方法的使用,常规做法是:先声明一个方法,再调用该方法。但我们并没有声明“textFieldShouldReturn:”方法,而是拿来就用。这类方法有一个特点,它是Delegate内的方法。确切地讲,这个方法是UITextFiled对象的Delegate所声明的一个方法。如果查文档,会发现UITextField的Delegate声明的多个方法,“textFieldShouldReturn:”仅仅是其中的一个较为重要的方法。

对于Delegate旗下的方法,只有将Delegate委托给某个ViewController之后,才能在被委托的ViewController中调用这个方法。

看到这里,自然就懂得了为什么textFieldShouldReturn:没有被调用。接下来,开始我们的第三步。

第三步:关联委托者与被委托者。在Storyboard中,完成委托者与被委托者的关联,可谓弹指一间。具体操作方法是,选中视图中的TextFiled控件,通过“Ctrl+drag”操作,引向Dock中的ViewController,从弹出框中,选择Delegate,如图3-11所示。图3-11 指定TextField的委托为ViewController

千万不要小看这一步,MVC设计模式在这一步发挥得淋漓尽致。正是这一操作,才将View(视图)与ViewController(视图控制器)关联起来。

完成以上操作后,再运行一下。当单击键盘的“完成”按钮时,键盘会自动消失。

让键盘消失的方法有四种,通过UITextFiledDelegate只是方法之一。通过事件响应,也能让键盘消失。3.8.2 键盘消失方法二:事件响应的调用

我们先来梳理下解题的思路,再按照思路,逐步编程。当用户单击键盘的“return”按钮时(对应的中文按钮为“完成”),会触发一个事件,这个事件的名称为“didEndOnExit:”,在这个事件响应中,向这个UITextFiled对象发送resignFirstResponder消息。总的来讲,需要三步来完成。为便于讲解,我们单独创建一个工程。

第一步:声明UITextField的“return”单击事件(IBAction)。打开Main.storyboard编辑页面,选中UITextField,通过“Ctrl+drag”操作,向ViewController.h文件的“@interface ViewController:UIViewController”下方拖拽。当出现一条蓝线时,松开鼠标。弹出一个小窗口,如图3-12所示。图3-12 创建TextField的didEndOnExit事件

不要小看一个“Ctrl+drag”操作,它可以做很多事情。仔细查看这个操作出现的每一项。Connection有三个选项:Outlet、Action和Outlet Connection。 Outlet是输出口的意思,通过Outlet可访问对象的属性; Action是事件,用于获取对象的某个事件的响应; Outlet Connection是输出口集合,当一个View上有多个同类的对

象时,会用到Outlet Connection。

在这个示例中,Connection的类型选中Action。同时,给这个事件起个名字。

还有一个重要的Event选项。在Event下拉框中,有十多个事件,从字面意思,也能猜出个大概。这里选择“Did End On Exit”(输入完成后,退出键盘)。

第二步:创建UITextField的输出口(IBOutlet)。选中UITextField,通过“Ctrl+drag”操作,向ViewController.h文件的“@interface ViewController:UIViewController”下方拖拽。当出现一条蓝线时,松开鼠标。弹出一个小窗口,如图3-13所示,这次的Connection选择Outlet。图3-13 创建TextField的Outlet

经过以上两步操作后,ViewController.h文件的代码如下:@interface ViewController : UIViewController-(IBAction)didEndOnExit:(id)sender;@property(weak, nonatomic) IBOutlet UITextField *textField;@end

其中,IBAction和IBOutlet这两行代码是通过“Ctrl+drag”操作自动生成的。

第三步:实现键盘“完成”按钮的单击响应事件。在对应的.m文件中,添加以下代码:-(IBAction)didEndOnExit:(id)sender{[self.textField resignFirstResponder];}

这行代码,是用来告知UITextFiled对象,释放第一响应者,不要再成为键盘的输入焦点了,这样一来,键盘就消失了。

以上讲解了两种让键盘消失的方法,从用户体验的角度来看,这两种方法有一个共同的特点,那就是,只有当用户单击键盘的“完成”按钮时,键盘才能消失。为了进一步提升用户体验,我们可以设想,只要用户单击一下非输入区域,键盘就会自动消失,那该多好啊。这里,我们将再介绍两种方法,来实现这个功能。3.8.3 键盘消失方法三:将UIView改为UIControl

这里重点讲述实现的思路,之前讲过的步骤,不再赘述。再次基于Single View创建一个新的工程。单击Main.storyboard,进入视图编辑页面,从对象库中拖拽一个UITextField,放到View中。

单击Document Outline中的View,在右侧的Identity Inspector中可看到,该View默认的Custom Class是UIView,如图3-14所示。图3-14 查看对象所属的类

此时,查看它的Connections Inspector,是看不到任何事件(Event)的。这里,做一个改动:将这个View的Class从下拉框中设为UIControl,此时,再查看它的Connections Inspector,发现有多个Event类型,如图3-15所示。图3-15 查看UIControl所拥有的触发事件

经过这个类的重新设置,之前的UIView变成了现在的UIControl。我们的期望是,在单击非编辑区域时,键盘消失。

第一步:将UIView改为UIControl。

第二步:通过“Ctrl+drag”操作,创建UITextFiled的IBOutlet,命名为textFiled;接着,声明UIControl的IBAction。选中Document Outline中的Control,通过“Ctrl+drag”操作,在对应的ViewController.h文件中,创建IBAction。弹出的窗口如图3-16所示。图3-16 设置UIControl的Target-Action

此时,ViewController.h中的代码如下:#import @interface ViewController : UIViewController@property (weak, nonatomic) IBOutlet UITextField *textField;-(IBAction)touchUpInsideAction:(id)sender;@end

第三步:实现UIControl的Touch Up Inside事件。在ViewController.m文件中,添加以下代码:-(IBAction)touchUpInsideAction:(id)sender{[self.textField resignFirstResponder];}

试着运行一下吧。完成内容输入后,单击编辑框以外的任何区域,键盘会自动消失。当然,单击键盘的“完成”按钮,键盘不会消失。这是因为,我们没有响应这个按钮单击事件。你完全可以参考上一个示例,实现这个功能。3.8.4 键盘消失方法四:巧用UIButton

如果仅仅是为了响应一个单击事件,UIButton是最易使用的控件。如果不想把UIView改为UIControl,那么,可以用UIButton来代替。在上一个示例中,之所以将UIView改为UIControl,是因为UIView无法响应单击事件。既然如此,我们就可以通过UIButton代替UIControl。

简单讲述下实现步骤。创建一个工程,从对象库中拖拽一个UIButton,放到Storyboard的View中。调整UIButton的尺寸,使之布满整个屏幕。

之后,拖拽一个UITextField,放在UIButton之上。这里需要注意一点:UITextField一定要在UIButton之上,否则的话,编辑框将无法接收输入的事件。

我们的目的是,通过单击这个巨型Button,让键盘消失。其实现思路是:在Button单击响应事件中,让UITextFiled对象释放第一响应者。

具体的实现,也是分三步。

第一步:通过“Ctrl+drag”操作,创建UITextField的IBOutlet。

第二步:通过“Ctrl+drag”操作,创建UIButton的IBAction。

第三步:实现巨型Button的IBAction事件。

经过以上三步,ViewController.h文件的代码如下:@interface ViewController : UIViewController@property (weak, nonatomic) IBOutlet UITextField *textField;-(IBAction)buttonAction:(id)sender;@end

对应的ViewController.m文件的代码如下:-(IBAction)buttonAction:(id)sender{[self.textField resignFirstResponder];}

试着运行下吧。完成输入后,单击编辑框之外的区域,键盘会自动消失。

3.9 UITextField更多用法

只要有输入框存在,就得处理如何让键盘消失的问题。当然,你可以仅仅实现单击“完成”按钮,来让键盘消失。但为了让用户有更好的体验,当单击非编辑区域时,键盘也应该消失。

通过UITextFieldDelegate和UIControl方案,实现键盘的消失,这种方案是一种大智慧;而巨型Button和didOnExit方法,虽然也能用,但从软件编程思想上看,只能算是一种小聪明罢了!

在处理键盘消失的方法时,UITextFieldDelegate应用得更多些,尤其是当一个页面上有多个UITextFiled时,使用UITextFieldDelegate,不需要额外的代码,很容易维护。

iOS开发中,最常用的控件莫过于UIButton、UILabel和UITextField。其中UITextField更为复杂些,这是我们第一次接触Delegate概念。

UITextField还有几个常用的Delegate方法。(1)当用户输入完成时,需获取到用户输入的内容;UITextField所提供的对应的Delegate方法是:-(void)textFieldDidEndEditing:(UITextField *)sender;(2)在输入时,UITextField的内容会发生变化,此时会发出一个通知“UITextFieldTextDidChangeNotification”。这个Delegate方法的应用场景是:在用户输入时,针对输入的内容,显示相应的操作。

3.10 getter与setter的应用场景

在iOS学习中,总有些概念让人捉摸不透,让人时而清楚,时而糊涂。我个人也时常经历这样的过程。@property就是这样的一个“让人爱,又让人恨”的概念。

对于iOS初学者,初次看到@property总觉得怪怪的。下意识地搜资料,大多资料的解释是:@property默认实现了getter和setter的功能。从字面意思理解,getter就是获取这个属性的值,而setter就是为这个属性赋值。

仅仅这么一个简单的解释,或许你还有些疑惑:既然getter和setter已经默认实现了,那还需要我关心什么呢?人家又说了,你可以重写getter和setter。这可倒好,你不说我还明白,你越说我越糊涂了。在什么场景下才需要重写getter和setter呢?

这次,暂不谈getter。这里给出一个详细的setter使用场景,并给出相应的代码实现。

场景描述:在一个View上,添加一个Button和Label;这个Label显示Button单击次数。每单击一次Button,Label所显示的值自动加1。

如果你一直跟着教程学习,实现这个功能再容易不过了。通常的做法是:在Button的Target-Action中,添加两行代码就可以了。//单击Button后,计数器加1self.iBtnCounter++;//Label显示Button单击的次数self.lblCounterDisplay.text =[NSString stringWithFormat:@"Button clicked counter is:%d",self.iBtnCounter];

这时候,运行App。你能看到Button被单击时,单击计数器会发生相应的变化。这是一个再简单的功能不过了。当然,我们的目的不是讲述如何实现这么一个小功能,而是看看setter的应用。

如果不考虑软件架构,单单就实现这么一个功能来说,是可以的。如果你是一个完美主义者,倒应该写出更为优雅的架构。那就是,通过重写属性iBtnCounter的setter,来实现Label的变化。

刚才的解决方案是,计数器的设置和Label的显示都是在Button的触摸事件中完成的。下一个解决问题的思路来源于“事件的触发和对象的赋值时序”。根据这个时序,计数器的设置在Button的触摸事件中完成,而Label的显示在计数器对象的setter中完成。代码示例如下://当为计数器赋值时,将触发计数器对象属性的setter方法-(void) setIBtnCounter:(int)iBtnCounter{_iBtnCounter = iBtnCounter;self.lblCounterDisplay.text =[NSString stringWithFormat:@"Button clicked counter is: %d",self.iBtnCounter];}

当遇到因数据发生变化,而触发新的事件时,比如TableView的更新,通过重写setter,会使得架构更加清晰。XCode提供了很多类似的机制,比如Block的使用。并不是非得用这些方法不可,但有一点是肯定的:当你有意识地使用XCode最新推出的框架时,你的代码会显得优雅很多。

@property的getter和setter方法的应用,在我看来,是一套“组合拳”,只有在清楚编译器时序的情况下,才能运用自如。也有人对此有质疑,说是代码的耦合性加大了。可谓“仁者见仁、智者见智”。

不管怎么是,@property是Objective-C中的一个“神秘”的概念,若想深入理解,需要不断地尝试,这是一个“悟”的过程!

3.11 总结与启发

XCode是一个图形化的编程环境,控件的使用“所见即所得”。在XCode中,我们称这些控件为“对象(Object)”。所有的对象,在Object Library中一览无余。如果对每个对象的使用都给出一个实例,那将占用大量的篇幅。更何况,开发一个App也用不到这么多的对象。我们只需要掌握对象的使用方法,就可做到一通百通。

每个对象,都是由属性(@property)和方法(类方法和实例方法)组成的。属性就是点操作,而方法就是中括号[ ]调用了。

先要有点儿心理准备,对象中的Delegate较为抽象,理解起来有些困难。为此,我们单独开辟了一章,从多个维度讲述Delegate的应用。

第4章 视图(UIView)与视图控制器(UIViewController)

4.1 如何创建一个基本的视图

UIView是一个最基本的View,我们最常用的UIButton、UILabel等都是UIView的子类。

创建一个UIView,很简单。先确定UIView的原点坐标(CGPoint)、设定View的宽和高(CGSize),再通过addsubview加载即可。我们来创建一个Label,效果如图4-1所示。

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载