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/ ...
随机推荐
- 收缩 虚拟硬盘 shrink vhd
在使用WIN2012 的Hyper-v的虚拟磁盘时, 有时需要将磁盘中未使用的控件收缩掉, 这时就需要使用Hyper-v磁盘工具的收缩功能. 如果使用Hyper-v磁盘工具, 不能对vhd虚拟磁盘进行 ...
- PostgreSQL Hot Standby的搭建
一. 简介: PG在9.*版本后热备提供了新的一个功能,那就是Stream Replication的读写分离,是PG高可用性的一个典型应用.这个功能在oracle中叫active d ...
- python基础使用
Python 标识符 在python里,标识符有字母.数字.下划线组成. 在python中,所有标识符可以包括英文.数字以及下划线(_),但不能以数字开头. python中的标识符是区分大小写的. 以 ...
- sp_sys_ERPTrigger_base
USE [GalaxyPointDB24]GO/****** Object: StoredProcedure [dbo].[sp_zy_Process_scrap] Script Date: ...
- 新注册域名greenopen.site,向专业道路进军
新注册域名greenopen.site,向专业道路进军,欢迎访问!届时可以通过 greenopen.site jsxyhelu.github.io 或者jsxyhelu.cnblogs.com 访问本 ...
- 开发与测试整体过程中的Git分支merge流程
开发与测试整体过程中的Git分支merge流程 Git分支merge之开发流程 首先在Gitlab上有个仓库存储着原始的项目代码,其中包含一个叫master的分支.然后可能按功能进行分配,由不同的开发 ...
- mvc ==》json url
mvc ==>json 这也是.net 4.0 与4.5的区别,4.0 自带 ,而4.5 则要自己加上去,微软说是 當然,最簡單的應變措施就是直接把這段設定加回去,不過,加回去之前請三思,這條設 ...
- flume ng之TailSource
在它里面自带了一个TailSource以及TailDirSource,这个Source是负责读取一个文件,并一行一行的发送到sink端,而在flume-ng 1.4.0里面没有自带TailSource ...
- zImage.img、ramdisk.img、system.img、userdata.img介绍及解包、打包方法
ramdisk.img system.img userdata.img介绍及解包.打包方法 Android 源码编译后,在out/target/product/generic下生成ramdisk.im ...
- CSocket客户端(TCP)
首先是UDP和TCP的区别: UDP是不连接服务器,每次发送数据的时候需要服务器的IP:而TCP是先连接服务器,保持常连接,然后直接发送不需要IP. 下面是TCP客户端: 1.新建项目,TestCSo ...