iOS开发------Widget(Today Extension)插件化开发

Swift:自定义mainstoryboard需要注意的细节 有时候我们不用main作为主要的mainstoryboard,这个时候就要修改一些配置,否者启动的时候会崩溃,或者启动后界面是黑色的 1 自定义storyboard的控制器类需要填写 2 ✅is initial view controller,也就是view前面的指示箭头 3 info.plist文件中修改:Main storyboard file base name = "customst 阅读详情
iOS10.0发布啦(貌似过去有点时间了吧 - -),在宏观带给我们使用体验的提升之外,更多的是带给iOS开发者一定的欣喜。

因为我们又要学习新东西来适配10啦。


博文所说的Widget(以下称之为拓展应用)并不是iOS10系统新推出的插件化应用(其实早在iOS8上就已经出现啦,只不过楼主是在iOS10发布之后才算真正的关注它,实在是惭愧呀)。iOS10之前它仅仅是存在于通知那一栏中,至于多隐蔽我就不说了吧。但在iOS10之后获得重生,地位获得了巨大的提升,从这点也不难看出苹果增加了对它的重视。尽管公司的App没有适配Widget,但作为一个“后知后觉”的iOS开发者,注意到了但不研究一下就说不过去了吧?

为了避免真实情况与博文的图不太符合,这里声明一下:楼主用的IDE是最新版的Xcode8.0(没办法,还是迫不及待的进行了升级0.0),可能会与其他版本的Xcode界面不太一样

博文中的所有代码:https://github.com/RITL/WidgetDemo(如果有用请star支持一下,感谢)

预览图

这里附上Widget Demo中完成后的预览图: 这里会稍有不同,如果使用Xcode7及之前版本IDE编译的应用(后面称作宿主应用),那么找到Widget的方法如图1;如果是Xcode8编译的宿主应用,那么可以直接通过3D Touch唤起Widget,当然通过第一种也是可以的。不过两者本质是一样的。

   


创建Widget Extension

1.首先创建一个新的Target: New->Target,Xcode8 会出现如下界面,选择Today Extension,命名为WidgetExtension:

2、创建完毕,则会出现如下文件夹,名字什么的不是问题,一般创建好的名字都为TodayViewController,我只不过是改了改名字而已O(∩_∩)O

3、这里啰嗦一句,虽然作为应用的拓展,但这两个应用是“独立”存在的,你也可以认为这拓展应用与宿主应用是两个完全独立的应用,这也就是说明在开发过程中会出现一些共享的问题,不过共享问题下面博文会有介绍。在此之前,对于拓展应用,我们也是要去开发者申请APP ID以及开发,发布证书的。

由于楼主只是为了学习,用了Xcode8的Automatically manager signing,它的作用是自动生成id以及证书。

作用细说一点就是:如果开发Team没有相应的APP ID,那么Xcode会自动生成APP ID; 如果没有创建相应的证书,那么它会自动创建证书 (当然,正常开发过程中,还是建议手动去创建ID以及配置证书吧)

4、证书都配置完毕,运行,添加Widget,就可以看到咱们的项目已经具备了Widget的拓展功能,默认的是MainInterface.storyboard上的内容啦:(我改了改Label上的字,O(∩_∩)O)


布局方式interface builder or coding

如果牵扯到UI绘制的方式,这里只需要调整一点东西即可。Demo中楼主选用的是使用storyboard完成快速布局,当然,如果开发者习惯使用代码来完成布局,依旧是可以的。需要对拓展应用的info.plist文件做如下操作:

使用interface builder

这个是默认的,如果修改了默认的storyboard,只需要将NSExtensionMainStoryboard的value修改成相应的storyboard名字即可

使用coding

首先将NSExtensionMainStoryboard字段删除,添加NSExtensionPrincipalClass字典,value为主控制器的类名即可。

使用这个方法不要忘记在todayViewController的ViewDidLoad中设置preferredContentSize属性调整大小。


数据共享

很多的时候我们需要Widget与宿主应用共享一些数据,想到数据共享,如果是单一的APP,我们的方法是很多的,比如单例,文件等形式,但由于拓展与宿主应用是两个完全独立的App,并且iOS应用基于沙盒的形式,所以一般的共享数据方法都是实现不了数据共享,这里就需要使用App Groups。

App Groups

1、首先需要在开发者网站注册一个App Groups

2、在 宿主应用 以及 拓展应用 中将App Groups打开,选中需要共享数据的group


两种共享数据的方式


使用UserDefaults共享数据

NSUserDefaults大家应该都是非常熟悉的了,通常用法就是

//获取UserDefaults的单例对象,完成对应用内相关数据的持久化储存
[NSUserDefaults standardUserDefaults];

正像之前所说,由于沙盒机制,拓展应用是不允许访问宿主应用的沙盒路径的,因此上述用法是不对的,需要搭配app group完成实例化UserDefaults,使用UserDefaults类进行数据共享楼主封装为RITL_ShareDataDefaultsManager

通过groups实例化UserDefaults对象的代码如下:

//组名
private static let groupIdentifier : String = "group.com.yue.WidgetTest"

/// 获得userDefualt对象
private class func __userDefault() -> UserDefaults
{
    return UserDefaults(suiteName: RITL_ShareDataDefaultsManager.groupIdentifier)!
}

存储数据方法如下,至于为什么会有open关键词(与public作用是一样的,只不过开发文档中新的API貌似都改为open了),因为楼主在Demo中将该文件分离出来了,需要实现”不同命名空间”代码共用,所以Swift默认的Internal作用域就显得权限不足了,至于如何分离下面会提及:

//存放数据的键值
private static let defaultKey : String = "com.yue.WidgetTest.value"

/// 保存数据
open class func saveData(_ value : String)
{
    //保存数据
    __userDefault().set(value, forKey: RITL_ShareDataDefaultsManager.defaultKey)
    __userDefault().synchronize()
}

获取数据的方法与保存数据很像:

/// 获取数据
open class func getData() -> String!
{
    //如果值为nil,表示没有存过值,返回默认的值
    let value = (__userDefault().value(forKey: RITL_ShareDataDefaultsManager.defaultKey))

    __userDefault().synchronize()

    guard value == nil else {

        return value as! String
    }

    return ""
}

因为是通过文件来生成,所以必须要在必要的时候对存储的数据进行删除,如下:

/// 清除数据
open class func clearData()
{
    __userDefault().removeSuite(named: RITL_ShareDataDefaultsManager.groupIdentifier)
    __userDefault().synchronize()
}


使用FileManager共享数据

第二种方法说本质的与第一种是一样的,因为他们都是通过在本地创建文件完成数据的共享,该功能的Demo中封装成了RITL_ShareDataFileManager

与第一种不同的就是,它不但要实例化对象,还需要获得保存数据的路径,如下:

//组名
private static let groupIdentifier : String = "group.com.yue.WidgetTest"

//存储的路径
private static let dataSavePathFile : String = "Library/Caches/widgetTest"

/// 获得存储的路径
private class func __fileManagerSavePath() -> URL
{
    //获得当前的组的路径
    var url = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: RITL_ShareDataFileManager.groupIdentifier)

    //返回拼接完毕的路径
    url?.appendPathComponent(RITL_ShareDataFileManager.dataSavePathFile)

    return url!
}

保存数据的方法,因为在Swift中有的方法是throw异常的,所以写法稍有不同,如下:

/// 保存数据
open class func saveData(_ value:String) -> Bool
{
    //进行存储
    do {
        try value.write(to: __fileManagerSavePath(), atomically: true, encoding: String.Encoding.utf8)

    } catch _ as NSError {//出错

        return false
    }
    return true
}

获取数据的方法只是读取存放的数据即可,当然Demo中存的是字符串,方法实现如下:

/// 获取数据
open class func getData() -> String
{
    //用于接收数据
    var value : String

    do {//读取数据
        try value = String(contentsOf: __fileManagerSavePath())

    } catch _ as NSError {

        return ""//有误输出空字符串
    }
    return value
}

必要时候不要忘记清除数据:

/// 清除数据
open class func clearData() -> Bool
{
//其实不太规范,应该先判断是否存在该文件,再进行删除
    do {//开始删除
        try FileManager.default.removeItem(at: __fileManagerSavePath())

    } catch _ as NSError{

        return false
    }
    return true
}


代码共享

这里为什么会有代码共享呢,如果上面两个存储的类写在了宿主应用目录下,那么宿主应用使用是没有问题的,but,这个时候拓展应用是获取不到这两个类的,当然,如果每个应用里都写一套不就可以了,虽然这样也能解决问题,但我很难用完美解决问题来形容他,因为这样不仅会出现命名,不好维护等众多问题,严重的时候还会带来很多问题,话不多说,如何共享代码呢?

使用Framework

这个问题在iOS8之后能够完美的用framework来解决,(如果有人问iOS7怎么办?请面壁3秒钟,Widget不是iOS8才对我们开放的么0.0)

1、与创建拓展一样,New->Target,选择Cocoa Touch Framework来创建framework,Demo中命名随便了一点,起名为RITLKit

2、将需要共享的代码从源项目的编译源中删除,添加到RITLKit中

3、将创建的framework都要链接到 宿主项目 以及 拓展应用 的Linked Frameworks and Libraries中,不要忘了,都要添加,不然可能会出现找不到文件的问题

4、这里提示一下,如果上面的步骤完成,但是在拓展中还是提示找不到文件,那么还需要做一个步骤,就是将我们的framework添加到拓展应用中Allow app extension API only选中即可,将如下:

5、以上步骤完毕之后,应该就可以在拓展以及宿主应用中实现代码共用了,但还有一点:

如果是ObjC项目,导入Objc的文件,只需使用#import"XX.h"导入即可,但是如果framework中含有Swift文件,使用#import "Project-Swift.h"是导入不进去项目的,可以使用@import RILTKit; 对创建的framework编译的文件进行导入,就可以使用Swift文件了,这件事在Demo中也已经实现。


Extension与宿主应用交互

通过点击Widget上的按钮来打开宿主应用并实现响应操作也是一种重要的交互手段,如何实现呢?

1、首先我们需要在宿主应用的Target->Info->URL Types中添加url Schemes

2、通过Widget来打开宿主应用,Demo中点击Widget中的按钮跳转至不同的界面,通过Widget打开宿主应用的操作如下:

/// 打开我的App
- (void)openMyApplication:(NSString *)title
{
    NSURL * url = [NSURL URLWithString:[NSString stringWithFormat:@"WidgetDemoOpenViewController://%@",title]];

   [self.extensionContext openURL:url completionHandler:^(BOOL success) {}];
}

3、宿主App通过AppDelegate中的响应openUrl的代理方法,接收信息并发出通知来响应全局:

/// 
-(BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
    if ([url.scheme isEqualToString:@"WidgetDemoOpenViewController"])
    {
        NSLog(@"host = %@",url.host);

        //发送通知
        [[NSNotificationCenter defaultCenter] postNotificationName:@"ExtenicationNotification" object:url.host];
    }
    return false;
}

4、宿主应用中响应通知的控制器接收通知即可,比如Demo中是主页进行跳转:

//添加获得拓展打开基础应用的通知
[[NSNotificationCenter defaultCenter] addObserverForName:@"ExtenicationNotification" object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {

    //获得类型
    NSString * type = note.object;

    [weakSelf presentTextController:type];

}];


NCWidgetProviding协议

如何仔细看,其实Widget的控制器与其他的控制器是没有区别的,只不过它履行了一个叫做”NCWidgetProviding”的协议。协议方法不多,在iOS10中新增了一个,废弃了一个,如下:

// 这个就不用多说了吧,没有很难得单词哦0.0
typedef NS_ENUM(NSUInteger, NCUpdateResult) {
    NCUpdateResultNewData,
    NCUpdateResultNoData,
    NCUpdateResultFailed
} NS_ENUM_AVAILABLE_IOS(8_0);


/* 该方法是用来告知Widget控制器是否需要更新的一个协议方法 */
- (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult result))completionHandler;

比如Demo中为了避免重复刷新做了如下操作:

- (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler {
    // Perform any setup necessary in order to update the view.

    // If an error is encountered, use NCUpdateResultFailed
    // If there's no update required, use NCUpdateResultNoData
    // If there's an update, use NCUpdateResultNewData
    //获得数据
    NSString * newValue = [RITL_ShareDataDefaultsManager getData];

    if ([newValue isEqualToString:self.textLabel.text])//表明没有更新
    {
        completionHandler(NCUpdateResultNoData);
    }

    else//需要刷新
    {
        completionHandler(NCUpdateResultNewData);
    }
}
// iOS10 版本之后将不会再被唤起
// 用来设置Widget控制器边框间距的方法,如果出现偏差,可以调整此方法的返回值进行操作
- (UIEdgeInsets)widgetMarginInsetsForProposedMarginInsets:(UIEdgeInsets)defaultMarginInsets NS_DEPRECATED_IOS(8_0, 10_0, "This method will not be called on widgets linked against iOS versions 10.0 and later.");


// iOS10 新增的方法
// 用来设置Widget是展开还是折叠状态的方法,可以设置相关的preferredContentSizes属性修改大小
- (void)widgetActiveDisplayModeDidChange:(NCWidgetDisplayMode)activeDisplayMode withMaximumSize:(CGSize)maxSize NS_AVAILABLE_IOS(10_0);


保存数据的时机

这个看具体的需求,比如Demo中就是选择在宿主应用将要失去Active状态的时候进行数据的保存,实现如下:

    //获得失去前台的监听
    [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillResignActiveNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {//进行数据的保存

        //保存当前的数据
#ifdef RITL_ShareDataType_UserDefaults
        //第一种保存数据
        [RITL_ShareDataDefaultsManager saveData:weakSelf.mainTextField.text];

#else
        //第二种保存数据
        [RITL_ShareDataFileManager saveData:weakSelf.mainTextField.text];
#endif

    }];


Widget无法展开折叠问题

2016-09-24补充

之前丢了一点,也有小伙伴们问,就是说按照上面的方式来开发插件,不能折叠的问题.

解决方案:

//在TodayViewController的ViewDidLoad里面需要设置最大展示的类型
#ifdef __IPHONE_10_0 //因为是iOS10才有的,还请记得适配
    //如果需要折叠
    self.extensionContext.widgetLargestAvailableDisplayMode = NCWidgetDisplayModeExpanded;
#endif


Widget高度范围

2016-09-28补充

首先感谢一下优快云用户qq_29012653的提示,之前没有太注意,在协议方法widgetActiveDisplayModeDidChange:withMaximumSize:打印了一下各种状态下的MaxSize:

// 5s模拟器下:
// NCWidgetDisplayModeCompact模式下:{304, 110}
// NCWidgetDisplayModeExpanded模式下:{304, 528}

// 6s模拟器下:
// NCWidgetDisplayModeCompact模式下:{359, 110}
// NCWidgetDisplayModeExpanded模式下:{359, 616}

//从上面来看,虽说是最大的Size,但不得不说,苹果还是把Widget的高度范围限制在了110 ~ 最大值之间
//如果设置高度小于110,那么default = 110;
//如果设置高度大于最大的616,那么default = 最大值;

但折叠状态下,可能是出于规范的考虑,苹果是果断的将高度固定在110上,所以这个时候我们设置preferredContentSize属性是没有任何效果的。

更多的欢迎下载Github代码一起钻研~3Q

感谢一下博文对我的帮助,感谢
iOS开发之widget实现
WWDC 2014 Session笔记 - iOS 通知中心扩展制作入门

构建iOS通知中心的Today Widget扩展 本文还有配套的精品资源,点击获取 简介:Today Widget扩展功能允许用户在iOS通知中心快速查看和操作应用内容。开发者通过在Xcode中创建Today Extension Target,实现NCWidgetProviding协议,与主应用进行数据共享,以及优化用户界面来增强用户体验。本文将详细探讨如何在iOS应用程序中开发和集成这种扩展,涵盖从创建扩展目标到数据通信... 阅读详情

相关推荐

基于Hadoop的NBA篮球球员大数据分析与可视化系统大数据分析系统python的项目(源码+lw+部署文档+讲解等)

本系统以 Hadoop 为基础,致力于对 NBA 篮球球员的海量数据进行深入分析与可视化呈现。它能够收集来自多个数据源的丰富数据,包括球员比赛统计数据、身体素质数据、技术特点数据等。利用 Hadoop 的分布式存储和计算能力,对这些大规模数据进行高效处理。通过数据分析算法,挖掘球员的潜在价值和特点,如球员的得分能力趋势、篮板球效率变化、不同位置球员的技术差异等。同时,借助先进的可视化技术,将分析结果以直观的图表、图形等形式展示出来。教练和球队管理层可以通过可视化界面清晰地了解球员的综合表现和发展态势,从而制

QQ3359892174的博客 1314

iOS 删除默认主页Main.storyboard及跳转到指定自定义主页

iOS 删除默认主页Main.storyboard及指定自定义主页 环境:Xcode V12.4 及 MacOS V11.2.1 一、删除默认主页Main.storyboard 1、删除Main.storyboard文件 2、选中项目----General ---- Deployment Info —— 清空Main Interface的Main,此时会自动删除Info.plist中的 <key>UIMainStoryboardFile</key> <string>Ma

zhaosuningzsn的博客 1055

别再只用串口助手了!用LabVIEW给STM32F103C8T6做个专属上位机(附完整源码)

本文详细介绍了如何利用LabVIEW为STM32F103C8T6开发定制化上位机,替代传统串口助手,实现高效数据交互。内容涵盖硬件架构设计、STM32固件优化、LabVIEW上位机开发技巧及工业级应用案例,特别适合需要实时数据采集与处理的工控场景。

weixin_42524762的博客 368

Xcode6中如何去掉默认的Main.storyboard

xcode 6取消了 Empty Application 模板来创建一个工程,创建出来的有工程多了Main.storyboard,默认加载Main.storyboard,但是有很多人还想用代码来实现UI的布局,去除Main.storyboard的有三步: 首先如图打开工程下面Supporting Files下面的Info.plist 删除下面所指的一行 其次找到工

gu_jin_xiao的专栏 628

ArcGIS中根据DEM和地形起伏度进行地貌分类划分

大家好我是GIS蒲公英,数字高程模型(digital elevation model, DEM)经常都被用到专题地图的底图中,下面来我们来交流一下仅选择海拔和地形起伏度两个指标,用DEM来进行地貌类型的自动判别划分

GIS蒲公英的博客 3万+

华为S5700三层交换机VLAN配置避坑指南:为什么你的VLAN间无法互通?

本文针对华为S5700三层交换机VLAN间无法互通的典型故障,提供深度排障指南。文章从数据转发原理入手,系统梳理了从物理链路、VLAN配置、VLANIF接口状态、路由表到ARP表、MTU及安全策略等全链路检查点,并给出了清晰的排查命令与流程,帮助网络工程师快速定位并解决VLAN间路由问题,实现稳定互通。

tt34567的博客 873

IOS8新特性NSExtensionMainStoryboard (扩展)

一个扩展并不是一个app,它的生命周期和运行环境不同于普通app。在生命周期方面,扩展的生命周期从用户在另一个app中选择了扩展开始,一直到扩展完成了用户的请求生命周期结束。在运行环境方面,扩展的限制要比普通app更严格,扩展的可用内存上限以及可用的API都比普通app要少。严格限制扩展的内存是因为在同一时间可能会有多个扩展同时运行,如Widget扩展。如果API声明包含NS_EXTENSION_

沸腾的泪水的专栏 2528

ROS2仿真抓取分拣案例,使用myCobot 280机械臂结合ROS2系统搭建机械分拣站

本案例展示了如何利用myCobot 280 urdf模型构建一个对象分类站,并在ROS2 jazzy中完成运动规划。

m0_71627844的博客 2125

iOS 8新特性之扩展(Extension)

iOS 8新特性之扩展   一、扩展概述 扩展(Extension)是iOS 8中引入的一个非常重要的新特性。扩展让app之间的数据交互成为可能。用户可以在app中使用其他应用提供的功能,而无需离开当前的应用。 在iOS 8系统之前,每一个app在物理上都是彼此独立的,app之间不能互访彼此的私有数据。   而在引入扩展之后,其他app可以与扩展进行数据交换。基于安全和性能的考虑,每一

sunyazhou13的专栏 1537

SolidWorks 小车底盘 装配体

通过制作小车底盘了解solidworks装配体的部分知识

及时总结 2228

不提交也不释放的事务,数据库连接还有可能被复用

摘要 实验发现Spring事务管理下,未提交事务的数据库连接池连接仍可被复用,这与传统认知相悖。通过配置HikariCP连接池(最大2连接)并设计测试接口,观察到未提交事务的连接会被后续请求复用。这可能导致GTID错误(当MySQL开启GTID且混用事务/非事务表时)以及数据修改丢失问题。研究揭示了Spring事务管理机制与连接池交互的特殊行为,对高并发系统设计具有重要启示。

蔡定努 177

关于iOS16以后WidgetExtension无法进行断点调试的问题

关于iOS16以后WidgetExtension无法进行断点调试的问题

写程序的柠檬精 2391

区块链技术概述:从比特币到Web3.0

区块链技术正重塑数字世界的信任机制。本文系统梳理了区块链的发展路径:从比特币的PoW共识和UTXO模型,到以太坊的智能合约和账户体系,直至Web3.0的去中心化互联网愿景。文章详细解析了区块链的核心特性(去中心化、不可篡改等)和数据结构,通过流程图展示了比特币交易生命周期和智能合约执行流程,并对比了Web2.0与Web3.0的关键差异。最后,用Python代码实现了包含区块类、交易处理和挖矿机制的简易区块链系统,为理解区块链底层原理提供了实践参考。

闲人编程的博客 456

IOS 开发记录(修改MainStoryBoard)

1 本来想换一个StoryBoard 不用默认的main。因为之前看的视频都是在 maininterface 中修改成 自定义的sb。但是我发现修改了以后根本没用。。 后来我发现了在info.plist 中。 这里还有个main。所以这里改成你自定以的Sb 就可以了。新版本的和之前的不一样。。。。 ...

天行健,君子以自强不息。 536

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

创建 iOS 小组件(Widget)

实际开发中,需要根据需求进一步自定义和优化小组件的功能和外观。

it_15077647703的博客 3268

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Today EXtension (ios13)

今日扩展 使用说明 创建方式:在项目创建后的情况下,点击 File -> new -> Target 然后在弹出的窗口中选择Today Extension。然后输入扩展的名称即可,完成后会以分组文件夹的形式显示到项目目录中。 他是系统的扩展,不是完整的app,需要有主要的app(宿主app)的存在才能够使用它。 一般用开将自己app中当天常用到的信息或者功能进行呈现,用户点击过后可快速唤起对应的主app,也可也针对不同的选项,跳转到app指定的页面。 用户在today extension中点击

weixin_43497251的博客 426

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

iOS Extension(应用扩展)-----Widget开发(附Demo)

extension是iOS8新开放的一种对几个固定系统区域的扩展机制,它可以在一定程度上弥补iOS的沙盒机制对应用间通信的限制。 extension的出现,为用户提供了在其它应用中使用我们应用提供的服务的便捷方式,比如用户可以在Today Extension中查看应用展示的简略信息,而不用再进到我们的应用中,同样可以快捷操作app的功能,这将是一种全新的用户体验。 现在新建一个项目,命名...

iOS野狼的专栏 1484

iOS-设置Main Interface通过storyboard或者xib加载主界面

当我们启动APP的单个视图应用程序,不是以编程方式从AppDelelgate分配rootViewController时,而是使用“Main Interface”主界面选项将storyboard或者xib指定为我的主界面文件 关键是初始化窗口并从storyboard或者xib本身设置UIApplication及其委托 >步骤1: 创建一个带有UIWindow对象的storyboard或者xib文件作为UIApplication的xib的所有者 >步骤2: 在xib中创建一个AppDe

MinggeQingchun的博客 1458

【IOS开发进阶系列 整理】Widget专题

1     机制原理1.1     概念1、app extension        app extension:extension是iOS8新开放的一种对几个固定系统区域的扩展机制,extension并不是一个独立的app,它有一个包含在app bundle中的独立bundle,extension的bundle后缀名是.appex。当然它有好多类型,如下图官方文档上介绍的那天,今天我们主要讲解的...

junbaozi的专栏 567

从0到1思考与实现iOS-Widget

讲述之前首先看下demo效果图: 基本的展开收起、本App本体交互 然后再展示几个效果不错的 Widget app 毒物 && Keep ESPN PCalc Musixmatch Fantastical 2 Carrot Weather d

cleven 689

ios 10 开发-使用 Extension创建iMessage App & Sticker Pack Application

第一. 介绍 随着iOS10的发布,苹果对开发者开放了Messages应用程序,开发人员现在可以创建他们自己的各种类型 并且可以内联iMessage内容的 iMessage apps,包含简单的表情包到可以交互的复杂界面。 在这个教程中, 我将会为你介绍新的framework--Messages framework, 并且为你展示如何创建属于自己的iMessage Apps;

zzzzzdddddxxxxx的专栏 4702

Swift语言Storyboard教程:第一部分

本文由CocoaChina翻译小组@TurtleFromMars翻译自:Storyboards Tutorial in Swift: Part 1。 更新记录: 该Storyboard教程由Caroline Begbie更新iOS 8和Swift相关内容。原文作者为教程编纂组的成员Matthijs Hollemans。 2014/12/10更新: 更新至Xcode 6.1.1。

lx31433的专栏 1424

Xcode -(Swift版)Storyboard教程 1.1

引言 1 - Storyboard 是一个能够节省你很多设计手机 App 界面时间的新特性,如图所示   (1)通过上图,你可以大致明白,如果你要制作一个页面很多很复杂的 App,Storyboard 可以帮助你解决写很多重复的跳转方法的麻烦,节省很多时间,以便你能够完全的专注于核心功能的实现上 2 - Storyboard 是最先在 iOS 5 引入的一项振奋人心的特性,大幅缩减构建 ...

weixin_33713350的博客 1212

iOS逆向(8)-Monkey、Logos

由于最近微信大佬发飙,罚了红包外挂5000万大洋,这就让人很慌了,别说罚我5000万,5000块我都吃不消。所以笔者决定以后不用微信做例子了。换成优酷了

weixin_34010949的博客 568

TodayWidget使用及创建

TodayWidget控件 这里说下很久之前项目中用到的技术,略微的研究了下,一直想写篇博文一直没有时间,现在项目处于维护期也就有了空闲时间,反过来再写发现已经有些忘记了,好在当时截了些图下来然后重新捋了一遍,决定把它写下来。 首先说下,这个没有接触的人肯能觉得比较陌生,其实这个控件呢,是大家比较熟悉的东西,这是Apple发布iOS8系统带来的有趣的新特性,这个App Extensions让第

WilonChen的博客 1520

对iOS10新增Api的详细探究

对iOS10新增Api的详细探究

liuyunge的博客 1295
上一篇: load方法和initialize方法的理解
下一篇: iOS10的推送框架UserNotifications以及UserNotificationsUI
RITL
博客等级 码龄11年 69粉丝 77原创
评论 8
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值