iOS - Widget 小部件
1、Widget
iOS extension 的出现,方便了用户查看应用的服务,比如用户可以在 Today 的 widgets 中查看应用的简略信息,然后点击进入相关的应用界面。

2、添加 Widget
添加 Widget 流程
- 1、添加 Today Extension
- 2、绘制 UI
- 3、调起 app
- 4 、数据共享
2.1 添加 Today Extension
在 Xcode菜单 => File => New => Target.. => 中选择 Today Extension



2.2 绘制 UI
创建 Today Extension 时会默认创建 MainInterface.storyboard,可以在里面绘制显示的 Widget 内容,也可以使用代码绘制 UI。

设置 Widget 展示视图的大小。关于 Widget 的背景色,以及具体展示的内容大家按需绘制。
- (void)viewDidLoad {
[super viewDidLoad]; // 设置 Widget 展示视图的大小
self.preferredContentSize = CGSizeMake([UIScreen mainScreen].bounds.size.width, 100); self.view.backgroundColor = [UIColor yellowColor];
}
在 iOS8 - iOS9 中运行程序后,会发现一个问题:绘制的内容与左侧边界有一定距离(约 30px)。如何解决这个问题呢,NCWidgetProviding 协议给出了解决方案。iOS10 中不存在这个问题。
// 实现 NCWidgetProviding 协议方法
- (UIEdgeInsets)widgetMarginInsetsForProposedMarginInsets:(UIEdgeInsets)defaultMarginInsets {
return UIEdgeInsetsMake(0, 0, 0, 0);
}
2.3 调起 app
因为 extension 和 containing app 是两个完全独立的进程,所以它们之间不能直接通信(不能像应用内部点击按钮,跳转到指定页面)。为了实现 Widget 调起 app,这里通过 openURL 的方式来启动 containing app。
在 containing app 中设置 URL Schemes

在 extension 的 ViewController 中添加如下代码。
// 通过 extensionContext 借助 host app 调起 app // TodayWidget 为在 containing app 中设置的 URL Schemes
[self.extensionContext openURL:[NSURL URLWithString:@"TodayWidget://"] completionHandler:^(BOOL success) { NSLog(@"open url result:%d", success);
}];
2.4 数据共享
通过 App Groups 提供的同一 group 内 app 共同读写区域,可以用 NSUserDefaults 和 NSFileManager 两种方式实现 extension 和 containing app 之间的数据共享。


- 通过 NSUserDefaults 共享数据
保存数据
- (void)saveDataByNSUserDefaults { NSUserDefaults *shared = [[NSUserDefaults alloc] initWithSuiteName:@"group.qianqianstudio.freeInHouse"];
[shared setObject:@"asdfasdf" forKey:@"widget"];
[shared synchronize];
}
读取数据
- (NSString *)readDataFromNSUserDefaults { NSUserDefaults *shared = [[NSUserDefaults alloc] initWithSuiteName:@"group.qianqianstudio.freeInHouse"];
NSString *value = [shared valueForKey:@"widget"];
return value;
}
- 通过 NSFileManager 共享数据
保存数据
- (BOOL)saveDataByNSFileManager { NSError * error = nil; NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.qianqianstudio.freeInHouse"];
containerURL = [containerURL URLByAppendingPathComponent:@"Library/Caches/widget"];
NSString *value = @"asdfasdfasf";
BOOL result = [value writeToURL:containerURL atomically:YES encoding:NSUTF8StringEncoding error:&error]; if (!result) {
NSLog(@"%@", error);
} else {
NSLog(@"save value:%@ success.", value);
} return result;
}
读取数据
- (NSString *)readDataByNSFileManager { NSError *error = nil; NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.qianqianstudio.freeInHouse"];
containerURL = [containerURL URLByAppendingPathComponent:@"Library/Caches/widget"];
NSString *value = [NSString stringWithContentsOfURL:containerURL encoding:NSUTF8StringEncoding error:& error]; return value;
}
3、设置 Widget
1、Widget 中展开、折叠
iOS10 在 NSExtensionContext 中,新添了 widgetLargestAvailableDisplayMode 属性,来确认当前 Widget 是展开还是折叠状态。所以,先在 viewWillAppear 中设置 Widget 的 mode 为展开。
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated]; self.extensionContext.widgetLargestAvailableDisplayMode = NCWidgetDisplayModeExpanded;
}
然后,就是展开和折叠的处理了。在 NCWidgetProviding 协议中,新添了这个方法 widgetActiveDisplayModeDidChange。
- (void)widgetActiveDisplayModeDidChange:(NCWidgetDisplayMode)activeDisplayMode withMaximumSize:(CGSize)maxSize { if (activeDisplayMode == NCWidgetDisplayModeCompact) {
self.preferredContentSize = CGSizeMake([UIScreen mainScreen].bounds.size.width, 110);
} else {
self.preferredContentSize = CGSizeMake([UIScreen mainScreen].bounds.size.width, 300);
}
}
2、启动 app 后,第一次显示的是折叠,而不是展开 ?
- 这个问题,归咎于 preferredContentSize 的设置,确认 Widget 的 mode 之前,不要设置这个值。在 widgetActiveDisplayModeDidChange 中设置展开或折叠状态下 Widget 的高度,iOS10 环境下在这里设置高度也就足够了。
3、为什么有时展开或折叠 "失灵" 了,没有对应的展开或折叠呢 ?
- 这个问题的前提,肯定是你展开、折叠对应的 Widget 高度不一样,只是看到了右上角按钮内容改变,但高度却没有变。
- 这个问题的原因在于,点击展开、折叠按钮修改了 Widget 的 mode 之后,却没有设置对应的高度 preferredContentSize。怎么办呢?mode 改变后,设置对应状态下的高度即可。
4、真机调试 Widget
真机调试 Widget,牵扯到配置 group。首先我们的宿主 app id 为 com.qianqianstudio.TodayWidget, Today widget 插件的 bundle id 为 com.qianqianstudio.TodayWidget.Widget 这里我们需要注意,widget 的 bundle id 必须以宿主 bundle id 作为前缀。然后它俩之间建立的 group id 为 group.qianqianstudio.freeInHouse(可以取任意名)。
-
Identifiers => App Groups 里添加一个 app group id 为:group.qianqianstudio.freeInHouse

创建 app id。创建的时候选择 Explict App ID,App Services 里面勾选上 App Groups

主 app

widget

配置 provisioning profile(这里不再赘述 certificate 的生成步骤,添加 device 等),此时因为选择了勾选了 App Groups 这个 service 的 app id,所以可以看到 enabled services 那里有此项。

iOS - Widget 小部件的更多相关文章
- Android简易实战教程--第十四话《模仿金山助手创建桌面Widget小部件》
打开谷歌api,对widget小部件做如下说明: App Widgets are miniature application views that can be embedded in otherap ...
- 从Hello World说起(Dart)到“几乎所有东西都是Widget”小部件。
import 'package:flutter/material.dart'; void main() => runApp(new MyApp()); class MyApp extends S ...
- Odoo14 自定义widget小部件
不多说先上源码. 1 odoo.define('my_company_users_widget', function (require) { 2 "use strict"; 3 4 ...
- Android Widget 小部件(一) 简单实现
在屏幕上加入Widget:或长按屏幕空白处,或找到WidgetPreview App选择. 原生系统4.0下面使用长按方式,4.0及以上 打开WIDGETS 创建Widget的一般步骤: 在menif ...
- Android Widget 小部件(四---完结) 使用ListView、GridView、StackView、ViewFlipper展示Widget
官方有话这样说: A RemoteViews object (and, consequently, an App Widget) can support the following layout cl ...
- django中widget小部件
1. 处理 input 的部件 TextInput NumberInput EmailInput URLInput PasswordInput HiddenInput DateInput Dat ...
- Android Widget 小部件(三) 在Activity中加入Widget
package com.stone.ui; import static android.util.Log.d; import android.app.Activity; import android. ...
- yii2小部件(widget)
一.创建一个简单的小部件 namespace common\components; //common需要自己先设定一个别名 use yii\base\Widget; //小部件需要继承的基类 use ...
- Android-RemoteView-桌面小部件
Android-RemoteView-桌面小部件 学习自 <Android开发艺术探索> https://developer.android.google.cn/guide/topics/ ...
随机推荐
- Readonly和disabled的区别 display:none和visible:hidden的区别
怎样使input中的内容为只读,也就是说不让用户更改里面的内容. <input type="text" name="input1" value=" ...
- 原来样式改变不了(input type="file")
label { background-color: #979fa8; color: #fff; display: inline-block; padding: .8rem 4rem; cursor: ...
- linux ubuntu12.04 解压中文zip文件,解压之后乱码
在windows下压缩后的zip包,在ubuntu下解压后显示为乱码问题 1.zip文件解压之后文件名乱码: 第一步 首先安装7zip和convmv(如果之前没有安装的话) 在命令行执行安装命令如下: ...
- java 用socket制作一个简易多人聊天室
代码: 服务器端Server import java.io.*; import java.net.*; import java.util.ArrayList; public class Server{ ...
- Ibatis的简单介绍
定义: 相对Hibernate和Apache OJB 等“一站式”ORM解决方案而言,ibatis 是一种“半自动化”的ORM实现.以前ORM的框架(hibernate,ojb)的局限: 1. 系统的 ...
- mysql一些常用命令总结
mysql时间戳转日期格式SELECT FROM_UNIXTIME(add_time,'%Y-%m-%d %H:%i:%s') FROM `wh5_username` where id=23; 按in ...
- 基于@AspectJ和schema的aop(二)---@AspectJ基础语法
@AspectJ使用jdk5.0和正规的AspectJ切点表达式描述切面, 由于spring只支持方法的连接点,所以Spring只支持部分AspectJ的切点语言. 1.切点表达式函数 AspectJ ...
- error:no such partition grub rescue
重新安装了ubuntu12.04后,Ubuntu开机就出现:error:no such partitiongrub rescue >一般情况下,出现这类错误是引导文件出错或者系统找不到引导文件, ...
- css公共样式,初始化
/* CSS Document */ body, button, select, textarea, input, label, option, fieldset, legend{font-famil ...
- PHP CURL实现远程下载文件到本地
<?php //$result=httpcopy('http://www.phpernote.com/image/logo.gif'); echo '<pre>';print_r($ ...