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 之间的数据共享。

    1. 通过 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;
      }
    1. 通过 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 小部件的更多相关文章

  1. Android简易实战教程--第十四话《模仿金山助手创建桌面Widget小部件》

    打开谷歌api,对widget小部件做如下说明: App Widgets are miniature application views that can be embedded in otherap ...

  2. 从Hello World说起(Dart)到“几乎所有东西都是Widget”小部件。

    import 'package:flutter/material.dart'; void main() => runApp(new MyApp()); class MyApp extends S ...

  3. Odoo14 自定义widget小部件

    不多说先上源码. 1 odoo.define('my_company_users_widget', function (require) { 2 "use strict"; 3 4 ...

  4. Android Widget 小部件(一) 简单实现

    在屏幕上加入Widget:或长按屏幕空白处,或找到WidgetPreview App选择. 原生系统4.0下面使用长按方式,4.0及以上 打开WIDGETS 创建Widget的一般步骤: 在menif ...

  5. Android Widget 小部件(四---完结) 使用ListView、GridView、StackView、ViewFlipper展示Widget

    官方有话这样说: A RemoteViews object (and, consequently, an App Widget) can support the following layout cl ...

  6. django中widget小部件

    1. 处理 input 的部件 TextInput    NumberInput EmailInput URLInput PasswordInput HiddenInput DateInput Dat ...

  7. Android Widget 小部件(三) 在Activity中加入Widget

    package com.stone.ui; import static android.util.Log.d; import android.app.Activity; import android. ...

  8. yii2小部件(widget)

    一.创建一个简单的小部件 namespace common\components; //common需要自己先设定一个别名 use yii\base\Widget; //小部件需要继承的基类 use ...

  9. Android-RemoteView-桌面小部件

    Android-RemoteView-桌面小部件 学习自 <Android开发艺术探索> https://developer.android.google.cn/guide/topics/ ...

随机推荐

  1. Ubuntu 14.04 安装 JDK 7.0

    1.新建jvm文件夹-解压 # mkdir /usr/lib/jvm # tar zxvf jdk-7u79-linux-x64.gz -C /usr/lib/jvm 2.设置环境变量,在/etc/p ...

  2. Shipyard远程API

    1.鉴权模块 1.1 Login,获取token POST  http://192.168.31.149:8080/auth/login Headers Content-Type: applicati ...

  3. PHP序列化以及反序列化系列[1]--PHP序列化格式的写法

    反序列化:对单一的已序列化的变量进行操作,将其转换回 PHP 的值(zval). PHP序列化方式 PHP在序列化的时候会将相应的变量以对应的键值进行储存. 将一个类序列化的话,处理代码主要的 文件: ...

  4. 【PHP设计模式 06_GuanChaZhe.php】观察者模式

    <?php /** * [观察者模式] * PHP5中提供了 观察者(observer) 和 被观察者(subject) 的接口 * 在手册搜索:SplSubject (PHP 5 >= ...

  5. 浏览器检测navigator 对象

    1.浏览器及版本号不同的浏览器支持的功能.属性和方法各有不同.比如IE 和Firefox 显示的页面可能就会有所略微不同. alert('浏览器名称:' + navigator.appName); a ...

  6. MS Sq l数据类型

    一.nchar : 适用于西文字符,一个字符占一个字节. 二.char: 可以中文,一个字符占两个字节. 三.var :可变. 四.varchar.nvarchar . 所以一般来说,如果含有中文字符 ...

  7. HDU 1715 大菲波数

    大菲波数 问题描述 : Fibonacci数列,定义如下: f(1)=f(2)=1 f(n)=f(n-1)+f(n-2)  n>=3. 计算第n项Fibonacci数值. 输入: 输入第一行为一 ...

  8. 中国行政区域(省,市,县)SQL

    数据库:Region CREATE DATABASE [Region] 表:Province CREATE TABLE [dbo].[Province](     [Id] [int] NOT NUL ...

  9. 2016年11月23日 星期三 --出埃及记 Exodus 20:14

    2016年11月23日 星期三 --出埃及记 Exodus 20:14 "You shall not commit adultery.不可奸淫.

  10. 双击Button按钮后执行的函数

    在MFC中,当在用户界面拖入一个button后,双击会进入cpp文件,实现一个按钮点击的方法如: void CtestcontorlDlg::OnBnClickedButton() { : } 理解: ...