原文地址: http://blog.5dscape.cn/post/2012-09-06/40038111026
一种新的方式来分发杂志
由于iPad的时代的开始,在平板电脑中最欣赏的功能之一就是看杂志和报纸的可能性。几乎所有的主要出版商(和大量的未成年人)创造了他们自己的杂志和报纸应用程序,并率先在内部试验已经长大日益更好的应用程序。除了大量的软件公司遵循这条道路,在主要的提供自己的排版系统和很多其他的初创企业只是存在提供服务,为他们的客户创建自定义的应用程序。
虽然苹果在应用时若干规定,如限制了In App Purchase的系统外采购的问题的可能性,或引入一个订阅机制,它的iOS 5之前表现主要作为观察员,与iBooks的限制了其自身的参与程序竞技场应用程序,专门用于书籍。但与iOS 5,我们协助苹果对引进一个连贯的方法论杂志和报纸应用程序,通过提供的功能,被称为第一个重要步骤报刊亭,并开发了一个框架,叫做报摊套件,它提供了一个公共集线器的所有杂志和报纸应用程序和一个新的完全不同的方式来与应用程序交互。它强调这一事实,即每个开发或出版商仍然可以呈现在优选的方式中的内容是很重要的,苹果目前不提供对内容呈现,除了通用HIG指引服务的过程的moltitude旨在提出任何具体的解决方案内容(PDF,图片,媒体)。我们不会感到惊讶,如果在接下来的未来,苹果将要求所有的应用程序是兼容的报刊亭有资格在App Store改变其SDK规则。
因此,如何从其他应用程序一个报刊亭的应用程序有什么不同?所有的图标呈现第一:一个报亭兼容应用将不会看到了它的图标的跳板,但不同的和可定制的图标将在被称为一个特殊的图标群被显示报亭。此图标将不会是典型的方形图标,而是这将是一本杂志的封面,此外此盖可定制,并保持与在店里提供当前最新一期一致。
另外一个应用程序,决定住在报刊亭背后的保护伞将有利于新问题的背景下载和设备可以获知,并接受特别的推送通知进行自动下载新的问题最终。
为了实现这些和其他一些特点,应用程序必须采取考虑的几个方面,从图标管理,推送通知,里面的代码和至iTunesConnect定义。在本教程中,我们会尝试从头开始构建一个简单的报亭兼容的应用程序。
应用程序设置
基本的应用程序
先决条件本教程是安装的XCode 4.2和iOS 5 SDK。即使大多数的新功能,可以在iPhone模拟器,推notifcations,并在本教程中的应用程序购买部分直接测试需要的iOS开发者账号。
因此,让我们先从一个简单的方法:开放的XCode并创建一个新的“空”的项目,随意选择iPhone和iPad之间的目标设备。然后创建一个新的视图控制器,称之为StoreViewController和应用程序委托里面实例化一个新的对象,这个控制器并把它一个新的导航控制器内。最后定义一个新的图标,你的项目中添加并在模拟器中运行它。最明显的结果是这样的,我们在跳板和应用程序显示一个空的屏幕上的图标:
启用的报亭
现在,我们需要指导,我们希望我们的应用程序能与书报亭兼容的系统。第一个要求是创建一个特殊的图标为报刊杂志:该图标是由一般的应用程序图标的不同它必须作为我们杂志的封面。在我们的例子中,我们准备了一个简单的130×190像素的图标和XCode的进口。此外,我们需要在应用程序中添加或修改三个新领域的Info.plist。第一个新的字段是UINewsstandapp必须添加并设置为YES。二是创建或修改CFBundleIcons加入报亭图标字典项。最后,如果我们想支持新资产的后台下载,我们需要添加报刊亭代码的应用程序后台功能。新添加的plist项目将出现在XML语法如下:
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
<key> CFBundleIconFiles </key> <array> <string> icon.png </string> <string> icon@2x.png </string> </array> <key> CFBundleIcons </key> <dict> <key> CFBundlePrimaryIcon </key> <dict> <key> CFBundleIconFiles </key> <array> <string> icon.png </string> <string> icon@2x.png </string> </array> <key> UIPrerenderedIcon </key> <false/> </dict> <key> UINewsstandIcon </key> <dict> <key> CFBundleIconFiles </key> <array> <string> icon_newsstand.png </string> </array> <key> UINewsstandBindingEdge </key> <string> UINewsstandBindingEdgeLeft </string> <key> UINewsstandBindingType </key> <string> UINewsstandBindingTypeMagazine </string> </dict> </dict> <key> UINewsstandApp </key> <true/> <key> UIBackgroundModes </key> <array> <string> newsstand-content </string> </array>
需要注意的是CFBundleIconFiles部分仍然存在向后兼容,但在新的iOS 5的语法已经被复制里面CFBundlePrimaryIcon第CFBundleIcons。这些新设置出现在XCode中,如下面,我们在这里显示的值,而不是键名的信息参数的身影,只是为了让你看到新的图标文件结构是如何打上一个iOS 5的标签:
让我们建立并运行应用程序,结果会出现如下图所示:我们原来的图标已经从跳板被删除,并与报刊亭组内部的封面般的图标更换; 如果我们打开组那么我们就可以在我们的应用程序图标,点击它移动到前台。需要注意的是经典的风格图标仍需要由系统,因为它们是在聚光灯下使用,推送通知,在App Store中。
显示和下载杂志
它的时间,现在开始编写一些代码。第一次操作是我们的项目链接到NewsstandKit框架。不要忘了导入NewsstandKit头在需要它的分类如下:
1
#import<NewsstandKit / NewsstandKit.h>
为简单起见,我们假设现有问题的列表(全部免费)里面,可以从出版商服务器下载一个plist文件。所以为宗旨,我们定义一个类叫做 出版商 将采取下载项目当前列表的照顾。一旦该项目已被下载的,他们将被呈现给用户。plist中是相当简单:它是字典的数组,每个字典提供的标识名称(唯一的),标题(对用户可见),出版日期和缩略图封面图片和内容的URL代表的问题(PDF文件)下载。
1 2 3 4 5 6 7 8 9 10 11 12
<字典> <键>名称</键> <字符串>杂志-0 </字符串> <键>标题</键> <字符串>杂志发行0 </string> <key> Date </key> <date> 2011-10-01T06:00:00Z </date> <key> Cover </key> <string> http://www.viggiosoft.com/media/data/blog/newsstand/magazine-0.png </string> <key> Content </key> <string> http://www.viggiosoft.com/media/data/blog/newsstand/magazine-0.pdf </string> </dict>
在启动时StoreViewController会从服务器上下载的问题列表,分析它,并会出现在表列表中。同时它会下载封面缩略图,并显示他们在表中。这是很典型的代码仍然不相关的报刊杂志,所以我不会详细解释。在装载的端视图将目前本身是这样的。你会简单地用自己的封面,并暗示,邀请挖掘下载在一起的项目列表。
现在,关于报刊亭框架中最有趣的事情之一是,该系统通过给我们访问称为共享类管理对我们来说问题库NKLibrary。一旦我们得到了图书馆,我们将遍历我们从服务器上下载的问题列表,我们将增加我们的问题纳入共享NK库。这可以通过使用NKLibrary的完成日期:addIssueWithName需要输入只有唯一的名称和发布日期问题,会返回一个NKIssue实例。需要注意的是这个数据是我们需要为我们的应用程序中的数据的一个子集(除了我们简单的问题字典包含显示标题,内容和覆盖的URL),所以我们需要维护我们自己的名单,并保持它在与NKLibrary内容同步。还认为,一旦问题已经被添加它将被永久保存到系统中的NK库库,即使在随后的应用程序的运行,这是因为操作系统需要维护所有的问题的状态:这意味着它们将被禁止使用相同的名称添加一个问题两次,这样做会引发异常,并会崩溃的应用程序。回到我们的代码,同时具有的问题,从发行商的服务器上下载,我们将只加入那些尚未添加更新NKLibrary问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// Publisher.m //“问题”是从出版商的服务器上下载的问题阵列- (无效)addIssuesInNewsstand { //共享NKLibrary例如NKLibrary * nkLib = [ NKLibrary sharedLibrary ]; //我们通过我们的问题,迭代和只添加一个没有在NK库yet [ issues enumerateObjectsUsingBlock: ^ ( id obj , NSUInteger idx , BOOL * stop ) { NSString * name = [( NSDictionary * ) obj objectForKey: @"Name" ]; NKIssue * nkIssue = [ nkLib issueWithName: name ]; if ( ! nkIssue ) { nkIssue = [ nkLib addIssueWithName: name date: [( NSDictionary * ) obj objectForKey: @"Date" ]]; } }]; }
注意我们管理的静态信息从与动态存在于NKLibrary信息发布者未来的方式。特别是,我们可以通过相应的链接给出版商的问题NKIssue由唯一的名称标识,这要归功于这个事实NKLibrary公开的方法:
- (NKIssue * )issueWithName: (的NSString * )的名字
多亏此链接,我们能够填充我们表格单元与静态和动态信息的混合。这种细胞观点逸岸比提供的其他 标题 信息(来自出版商的列表中检索)将显示一个动作的标签,这将是“TAP TO下载”或“TAP TO READ”,根据该杂志已经下载了这个事实。此外,如果该问题正在下载,我们将展示,而不是“TAP”消息栏将显示下载进度。我们知道,通过检查NKIssue杂志状态 的状态 属性,它有三个可能的值: NKIssueContentStatusAvailable , NKIssueContentStatusDownloading 或 NKIssueContentStatusNone 。
下载问题
现在,让我们来探讨如何下载的问题发生,而应用程序是在前台。这将通过引入一个新类,名为做NKAssetDownload。首先是几个重要的概念:
-
每个问题是由一个单一的资产(如本例:我们只是有一个PDF文件)或多个资产(如:PDF,图片,视频,缩略图); 在后一种情况下,建议是包装在单个拉链的所有文件,然后下载这个单一文件。
-
最后本地目标路径由报亭框架建立和由该应用沙箱缓存内的系统分配的目录中,所以符合要求的iCloud
-
这种本地目标可以从NKIssue的检索contentURL财产
-
我们可以通过附加到路径contentURL我们自己的文件名,建立我们的最终目标路径,正如我们在一块下面的代码所做的:
1 2 3
- ( NSString * ) downloadPathForIssue: ( NKIssue * ) nkIssue { return [[ nkIssue . contentURL path ] stringByAppendingPathComponent: @"magazine.pdf" ]; }
因此,在我们的代码,当一个表格单元格被窃听和相应的问题也尚未下载,我们需要开始下载。下面的代码检索NKIssue对象,然后要求出版商实例的原始服务器URL在哪里下载的问题(在更复杂的应 用程序这一请求需要,例如,返回的下载URL之前In App Purchase的收据验证)最后,我们会要求NKIssue为将排队等待下载一个新的NKAssetDownload对象。请注意,我们存储在NKAssetDownload 用户信息属性表视图单元格的实际指数。作为我们的下载期间,我们想要更新只能通过查询表格中的右单元被检索的进度条,这是必需。即使这USERINFO是一本字典的存储限制的plist-有效对象而已,刑罚将是一个运行时异常,所以一定要小心!
1 2 3 4 5 6 7 8 9 10 11 12 13 14
//让我们找回 让我们出版商的服务器URL(存储在问题的plist)NSURL * downloadURL = [ 出版商contentURLForIssueWithName:nkIssue 。名]; 如果(!downloadURL )回报; //让我们创建一个请求和NKAssetDownload 让我们开始下载[ assetDownload downloadWithDelegate:自我]
在上面的代码中,我们设置assetDownlaod 委托财产的自我。这允许视图控制器监视下载进度实施NKURLConnectionDownloadDelegate这是已经公知的延伸(或专门化)NSURLConnectionDelegate协议。该协议定义了三种方法:
1 2 3
– connection:didWriteData:totalBytesWritten:expectedTotalBytes: – connectionDidResumeDownloading:totalBytesWritten:expectedTotalBytes: – connectionDidFinishDownloading:destinationURL:
第一种方法使我们能够监督我们下载的进展情况。由于连接的newsstandAssetDownload财产,我们可以检索到相关的连接NKAssetDownload,然后我们就可以获取与该资产杂志得益于表视图单元格指数,我们以前存储在属性用户信息词典:
1 2 3 4 5 6 7 8 9 10 11 12
- ( void ) connection: ( NSURLConnection * ) connection didWriteData: ( long long ) bytesWritten totalBytesWritten: ( long long ) totalBytesWritten expectedTotalBytes: ( long long ) expectedTotalBytes { [ self updateProgressOfConnection: connection withTotalBytesWritten: totalBytesWritten expectedTotalBytes: expectedTotalBytes ]; } - ( void ) updateProgressOfConnection: ( NSURLConnection * ) connection withTotalBytesWritten: ( long long ) totalBytesWritten expectedTotalBytes: ( long long ) expectedTotalBytes { //得到asset NKAssetDownload * dnl = connection . newsstandAssetDownload ; UITableViewCell * cell = [ table_ cellForRowAtIndexPath: [ NSIndexPath indexPathForRow: [[ dnl . userInfo objectForKey: @"Index" ] intValue ] inSection: 0 ]]; UIProgressView * progressView = ( UIProgressView * )[ cell viewWithTag: 102 ]; progressView . alpha = 1.0 ; [[ cell viewWithTag: 103 ] setAlpha: 0.0 ]; progressView . progress = 1.f * totalBytesWritten / expectedTotalBytes ; }
虽然进步法的实施是可选的,一切的一切,我们都没有严格要求,以监控进展情况,在接下来的两个方法必须执行。一种方法告诉我们,资产的下载已经恢复,我们可以更新我们的进展; 我们将看到通过更新进度条这个细节后,暂时我们正好表现为在上面的方法。另一种方法,而不是需要被实施为它通知下载完成委托以及所下载的内容(当前存储在临时区域)可以被移动到其最终位置(即,我们知道的是给我们witth所述contentURL属性的NKIssue:我们的实现,那么移动新的内容到它的最终位置并通过移除进度条,并用新的更换“TAP TO READ”消息刷新表
1 2 3 4 5 6 7 8 9 10 11
文件复制到目标 从%@到%@“复制文件,DESTINATIONURL ,的contentPath ;)} [ table_ reloadData ]; }
如果您的问题是由多个资产,当然你也可以排队它们。在这种情况下比监测单一资产下载其他(至少他们在复制他们的最终目标目录),你可能有兴趣知道,当所有排队的下载已经完成资产。在这种情况下,你可以注册到NKIssueDownloadCompletedNotification将发布(无用户信息),由有关的问题。
一个有趣的问题,我们想强调的是,我们正在下载该杂志的方式涵盖了我们的餐桌。如果你看一下代码,你会看到,只要控制器需要提供的表格单元格视图图像,它将查询Publisher类发送盖的索引进行检索,并完成处理程序块将由出版商实例来执行。出版商例如逸岸做封面数据的异步下载完成后运行,将更新表块。这完美的作品,但它可以以不同的方式使用报刊亭能力来完成:逸岸,如果你认为像盖,毕竟,一个问题的资产,你仍然可以添加该资产请求,系统在连接代理您可以重新加载受影响的电池与新项目。我们没有包括这方面的这段代码,因为我们宁愿专注于内容下载,但你可以考虑为你的应用中,这种做法是更优雅。
后台下载
之一的报亭的最有趣的特性是,如果该应用程序被暂停一次资产下载已经开始它将继续连(即:没有运行,但仍然在存储器中),或者它被终止。的过程中,而您的应用程序被暂停,将不会收到任何状态更新,但它会被唤醒的背景下,以处理下载完成操作:在这种情况下,你必须将下载的资产复制其最终目标的机会或者,对于压缩的内容,以解包它。
在的情况下的应用程序已经终止,同时下载正在进行时,情况就不同了。逸岸在完成下载应用程序的事件不能被简单地唤醒和连接委托完成的下载方法调用,当一个应用程序被终止它的应用程序委托对象已经不存在了。在这种情况下,该系统将重新启动在后台应用程序,然后将UIApplicationDelegate 应用:didFinishLaunchingWithOptions:将被称为与UIApplicationLaunchOptionsNewsstandDownloadsKey在密钥launchOptions字典。如果定义,该密钥将包含所有资产标识符导致推出的数组。从我的测试中,它似乎没有,如果你重新挂起下载会被下一个段落所解释该检查是真正需要的。
另一个需要考虑的重要的事情是,应用程序是每次重新启动时,我们需要检查现有挂起下载。如果有任何那么我们就必须重新连接到下载委托。如果我们不这样做,那么所有未重新连接下载的视为放弃该系统,他们将被取消:
1 2 3 4 5
//里面的应用程序委托的应用程序:didFinishLaunchingWithOptions:后视图控制器已经initialized NKLibrary * nkLib = [ NKLibrary sharedLibrary ]; for ( NKAssetDownload * asset in [ nkLib downloadingAssets ]) { [ asset downloadWithDelegate: store ]; }
新内容通知
一到报刊亭它的可能性,以接收推送通知时,新的内容可涉及最有趣的特点:这意味着该应用程序将能够在后台自动下载新的内容,并为出现在报刊亭组中更新的封面。
此功能的实现需要一些初步的设置。我不会有推送通知在您的应用程序工作所需的操作的细节进入,所以我会限制自己总结所需的步骤:
-
在开发者门户网站,打造一个Bundle ID特定的为您的应用程序,无需通配符
-
在开发者门户网站,使包ID为推送通知,并创建服务器证书
-
安装在您的推送通知服务器的服务器证书。如果你没有一个,你可以参考外部服务,如城市飞艇(这是免费的开发和释放一些限制生产)
-
检查你的Info.plist的包ID在你的代码是您在开发者门户网站使用的相同
-
在开发者门户网站创建一个配置文件配置为应用程序,并用它编译并在您的设备上安装应用程序时
它最初提出来测试,如果推送通知工作。因此,注册应用程序接受基础推送通知和推送服务器尝试发送邮件。如果你得到它,你就可以继续报亭整合。
1 2 3 4 5
// APNS标准登记在里面添加application:didFinishLaunchingWithOptions: [[ UIApplication sharedApplication ] registerForRemoteNotificationTypes: ( UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert )]; }
报摊套件增加了一个新的远程通知类型,称为UIRemoteNotificationTypeNewsstandContentAvailability它通知该应用,这是能够通过推送通知来接收新的内容的更新。由于我们的应用程序是不是对收到的其他类型的推送通知,我们可以替换上面的代码:
1 2 3
// APNS标准登记在里面添加application:didFinishLaunchingWithOptions: [[ UIApplication sharedApplication ] registerForRemoteNotificationTypes: UIRemoteNotificationTypeNewsstandContentAvailability ]; }
当你启动应用程序在第一时间,你会得到一个新的警报。如果你没有得到它,然后你的设置是无效的,我建议实施和检查传递的错误信息- (无效)的应用程序:(*的UIApplication)应用didFailToRegisterForRemoteNotificationsWithError:(NSError *)的错误方法。
在这一点上,你就可以发送推送通知到您的设备告知书报亭,一个新的问题是可用的。推送通知消息具有相同的语法与有效载荷必须包含唯一的区别标准的推送通知的内容可用键设置为1:
1 2 3 4 5 6
{ "aps" : { "content-available" : 1 , }, "device_tokens" : [ "E9623F5CFDE92B40DA4AA90B97B70428BCD8FFCBA067BE17A6EF5102651E66E9" ] }
有一个限制进口的相关推的频率:限制因后台下载(其中,顺便说一句,我们只有当设备连接到Wi-Fi网络发生)资源的消耗报亭推送通知是聚结和只有一个后台下载是每人每天允许的。出于测试目的,你可以通过设置用户默认值(这样做在启动时在你的应用程序委托)适当的键删除此限制:
1
[[ NSUserDefaults standardUserDefaults ] setBool: YES forKey: @"NKDontThrottleNewsstandContentNotifications" ];
当发送通知您的应用程序可以有两种可能的状态:在前台或没有运行。在第一种情况下,你必须应对UIApplicationDelegate 应用程序:didReceiveRemoteNotification:和你的行动可能是简单地提醒用户或自动启动新的下载或要求服务器在新一期的数据(标题,封面等)。在其他情况下你的应用程序会在后台再次调用激活didFinishLaunchingWithOptions:应用程序,哪些该方法做,除了其他东西是干什么的,是来检查的启动选项UIApplicationLaunchOptionsRemoteNotificationKey和检索的有效负载。在这种情况下,什么都可以做的是启动一个后台任务,将得到来自所有问题的列表,并开始下载的最后一个例如服务器获取最新问题。通常这样的背景任务应该在几秒钟,这是由操作系统分配开始自动下载的时间内完成。如果此时没有足够的考虑,因为你之前查询您的服务器,那么建议的方法是运行这个程序里面beginBackgroundTaskWithExpirationHandler:拨打从而获得约10分钟,整个工作。
下面的例子显示了应用程序如何表现得情况下,它没有运行(无论是在前台,也为无效)。在这个简单的例子,我们假设最新的杂志是“杂志-4”(但我们可以给名字的通知有效载荷内),然后就安排下载。在这一点上,应用程序将再次终止,但是当下载终止它被恢复,由于NK细胞的内部规则先前所解释的,然后将储器移动到其最终位置。
1 2 3 4 5 6 7 8 9 10 11
NSDictionary * payload = [ launchOptions objectForKey: UIApplicationLaunchOptionsRemoteNotificationKey ]; if ( payload ) { //时间表的问题在下载background NKIssue * issue4 = [[ NKLibrary sharedLibrary ] issueWithName: @"Magazine-4" ]; if ( issue4 ) { NSURL * downloadURL = [ NSURL URLWithString: @"http://www.viggiosoft.com/media/data/blog/newsstand/magazine-4.pdf" ]; NSURLRequest * req = [ NSURLRequest requestWithURL: downloadURL ]; NKAssetDownload * assetDownload = [ issue4 addAssetWithRequest: req ]; [ assetDownload downloadWithDelegate: store ]; } }
更新报亭图标
与书报亭推出了新的特点是根据上次更新封面更新报刊亭的应用程序图标的可能性。从苹果的建议是使用最后更新问题的盖子。另一个有趣的可能性是,我们可以标记一个图标新绦标记的问题是新 的,尚未阅读呢。当然,一旦应用程序重新启动或新的问题已经阅读它是很好的做法,删除此标记。我们可以改变图标设置窗扇可见的方式是很容易的,只要把这段代码后,地方你的下载没有完成,你的问题已经被安全地保存在其目标路径(从未展现盖可用之前它!):
1 2 3 4 5 6
//更新报亭icon UIImage * img = [ publisher coverImageForIssue: nkIssue ]; if ( img ) { [[ UIApplication sharedApplication ] setNewsstandIconImage: img ]; [[ UIApplication sharedApplication ] setApplicationIconBadgeNumber: 1 ]; }
你的屏幕上会出现这样的:大众品牌的报亭图标,你的应用程序图标覆盖更新到新的封面,并在它上面的“新”窗扇。
结论
在接下来的文章中,我们将谈论的报亭应用与In App Purchase的制度和如何使用iTunesConnect服务来管理它的交互。完整的源代码中使用超过一个参考,以更好地遵循本教程不是一个真实的例子来运行,可以在GitHub上找到。
在这一点上,你可能会问自己,如果加上报刊杂志为你的下一个应用程序有道理与否。有几个优点,但也缺点。的主要缺点是,书报亭工程已迁移到iOS5的设备。在写这篇文章的时候,在从iOS 5的分配几天给客户,通过率是1/3。我们可能在2011年年底的采用率大于80%的期望。开始提供与报刊杂志一本杂志的应用程序之前,无论如何考虑到这一点,如果你这样做是为一个客户,然后跟她谈话/他谈了这一点。如果你有在App Store中已有一本杂志,你正在考虑将其迁移到报摊,那么你当然不会失去现有客户群发布的更新不会影响已安装了以前版本的应用程序的所有用户,提供你不改变你的后端,即服务器端Web服务保持与旧的应用程序版本兼容。显然还停留为iOS4的潜在用户将无法安装新的iOS 5版本。
在使用报亭的主要优点是,从图的用户体验的点,它是作为用户感觉更自然从单个支架挑其杂志改善; 除了一些功能作为最新的封面,并自动下载将不胜感激。但从其在报刊杂志的应用程序将带来营销优势,因为报亭介绍了自己在店里一个单独的区域出版商的角度来看,非常明显,深受苹果的推广。最后,但从开发商的角度来看,你将能够采取的许多功能优势,包括一个共享的问题库和高效的后台下载的系统,其发展从无到有是很困难的,需要照顾的很多细节:现在一些可能存在的问题是由框架管理,您作为开发者,可以花更多的时间在提供良好的用户体验