iOS-为项目添加widget(手机最左侧页面的小工具)

2019-08-06  本文已影响0人  Y_3c23

Widget开发步骤

建议先阅读Widget的配置篇,再开始开发,因为开发的过程中需要提前准备一些东西【iOS】Widget开发-配置篇

1. 创建新的Target,选择Today Extension。点击Next后,会生成如下图的几个文件

image image

2. 查看新生成的Info.plist

Bundle display name:Widget在通知栏显示的名称

NSExtension

如果你是使用纯代码进行开发,请按照下面进行操作:

(1)请删除NSExtensionMainStoryboard 的键值对和MainInterface.storyboard文件;

(2)请添加NSExtensionPrincipalClass这个key,并将value设置为控制器(如TodayViewController)

3. 准备工作都已经完成,正式进入开发工作

iOS 8

①、iOS8下没有折叠和展开功能,默认的Widget高度为self.preferredContentSize设置的高度。

self.preferredContentSize =CGSizeMake(kScreenW,100);

②、iOS8下所有组件默认右移30单位,可以通过下面的方法修改上下左右的距离

- (UIEdgeInsets)widgetMarginInsetsForProposedMarginInsets:(UIEdgeInsets)defaultMarginInsets {returnUIEdgeInsetsMake(0,0,0,0);}

iOS 10

iOS 10以后,Widget可玩性更高了,有了两种显示模式

NCWidgetDisplayModeCompact, // Fixed height,高度固定,最低高度为110

NCWidgetDisplayModeExpanded, // Variable height,高度可变

// 5s模拟器下:

// NCWidgetDisplayModeCompact模式下:{304, 110}

// NCWidgetDisplayModeExpanded模式下:{304, 528}

// 6s模拟器下:

// NCWidgetDisplayModeCompact模式下:{359, 110}

// NCWidgetDisplayModeExpanded模式下:{359, 616}

设定显示模式,需要在设定Size前设定这个属性!!!

if([[UIDevicecurrentDevice] systemVersion].intValue >=10) {

self.extensionContext.widgetLargestAvailableDisplayMode = NCWidgetDisplayModeCompact;

// self.extensionContext.widgetLargestAvailableDisplayMode = NCWidgetDisplayModeExpanded;

}

viewDidLoad的代码如下:

- (void)viewDidLoad { 

  [superviewDidLoad];

if([[UIDevicecurrentDevice] systemVersion].intValue >=10) {

self.extensionContext.widgetLargestAvailableDisplayMode = NCWidgetDisplayModeCompact;   

}

self.preferredContentSize =CGSizeMake(kScreenW,100);     

  [selfsetupUI];

}

当显示模式设置为NCWidgetDisplayModeExpanded时,点击折叠和打开时,会触发下面这个方法,在这个方法中可以修改对应状态的高度

- (void)widgetActiveDisplayModeDidChange:(NCWidgetDisplayMode)activeDisplayMode withMaximumSize:(CGSize)maxSize {

if(activeDisplayMode == NCWidgetDisplayModeCompact) {

self.preferredContentSize =CGSizeMake(maxSize.width,110); 

  }else{

self.preferredContentSize =CGSizeMake(maxSize.width,200);   

}

}

在下面的方法中更新视图

-(void)widgetPerformUpdateWithCompletionHandler:(void(^)(NCUpdateResult))completionHandler {

//    NCUpdateResultNewData  新的内容需要重新绘制视图

//    NCUpdateResultNoData    部件不需要更新

//    NCUpdateResultFailed    更新过程中发生错误

completionHandler(NCUpdateResultNoData);

}

开发中可能会遇上的一些问题

代码共享

目前我见到了四种共享代码的方法:

将代码打包成Framework,然后link到主App和Widget中(推荐)

不怕安装包变大的话,可以考虑将需要的第三方库在主App和Widget中分别复制一份(推荐)

将需要共享的文件按图中进行勾选配置

image

通过Pods导入,不太建议通过pod分别向两个Target中导入第三方库,因为很容易发生一些不好处理的问题

数据共享

数据共享有两种常用的方法:

NSUserDefaults,和我们常用的方法一样,不过在创建NSUserDefaults时,需要填写我们之前的GroupID。通过GroupID,我们就可以进行主App和Widget之间的数据共享了。

/// 写入数据

NSString*groupID =@"group.com.aaa.bbb";

NSUserDefaults*ud = [[NSUserDefaultsalloc] initWithSuiteName:groupID];

[ud setObject:@"我是测试的数据"forKey:@"test"];

[ud synchronize];

/// 读取数据

NSString*groupID =@"group.com.aaa.bbb";

NSUserDefaults*ud = [[NSUserDefaultsalloc] initWithSuiteName:groupID];

NSString*value = [ud objectForKey:@"test"];

NSFileManager

/// 写入数据

NSString*groupID =@"group.com.aaa.bbb";

NSError*err =nil;

NSURL*containerURL = [[NSFileManagerdefaultManager] containerURLForSecurityApplicationGroupIdentifier:groupID];

containerURL = [containerURL URLByAppendingPathComponent:@"Library/Caches/test"];

NSString*value =@"我是测试的数据";

BOOLresult = [value writeToURL:containerURL atomically:YESencoding:NSUTF8StringEncodingerror:&err];

if(result){

NSLog(@"写入成功");

}

/// 读取数据

NSString*groupID =@"group.com.aaa.bbb";

NSError*err =nil;

NSURL*containerURL = [[NSFileManagerdefaultManager] containerURLForSecurityApplicationGroupIdentifier:groupID];

containerURL = [containerURL URLByAppendingPathComponent:@"Library/Caches/test"];

NSString*value = [NSStringstringWithContentsOfURL:containerURL encoding:NSUTF8StringEncodingerror:&err];

打开App

设置App的URLSchemes,打开APP主要通过URLScheme打开和传递参数值。

设置URLSchemes时,要独特一些,避免与其他App重复

image

在Widget中添加点击事件,用于触发打开App的操作和传递参数

NSString*schemeString =@"zhangpeng://actionName?paramName=paramValue";

[self.extensionContext openURL:[NSURLURLWithString:schemeString] completionHandler:^(BOOLsuccess) {

}];

app的appdelegate的代理方法中,截取URL,做响应处理:

// 所有版本的都可以使用

- (BOOL)application:(UIApplication*)application openURL:(NSURL*)url sourceApplication:(NSString*)sourceApplication annotation:(id)annotation {   

    [selfappCallbackWithOpenUrl:url];returnYES;

}

/// iOS 8 以后

- (BOOL)application:(UIApplication*)app openURL:(NSURL*)url options:(NSDictionary *)options {   

    [selfappCallbackWithOpenUrl:url];

returnYES;

}

/// iOS 7

- (BOOL)application:(UIApplication*)application handleOpenURL:(NSURL*)url {   

    [selfappCallbackWithOpenUrl:url];

returnYES;

}

- (void)appCallbackWithOpenUrl:(NSURL*)url{

NSLog(@"url: %@", url.host);

// 针对url进行不同的操作

}

数据刷新

当widget从屏幕上消失2s左右,再次出现在屏幕中时,都会重新调用viewDidLoad方法。所以每次出现都请求最新数据,进行刷新操作,widget都会闪一下,根据产品需求,可以做一下控制;

- (void)viewDidLoad { 

  [superviewDidLoad];

}

如果短时间内让Widget频繁地消失显示,那只会执行viewWillAppear方法;

- (void)viewWillAppear:(BOOL)animated {

    [superviewWillAppear:animated];

}

上一篇下一篇

猜你喜欢

热点阅读