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. webapi获取请求地址的IP

    References required: HttpContextWrapper - System.Web.dll RemoteEndpointMessageProperty - System.Serv ...

  2. 【原创】JDK动态代理,此次之后,永生难忘。

    动态代理,这个词在Java的世界里面经常被提起,尤其是对于部分(这里强调“部分”二字,因为有做了一两年就成大神的,实力强的令人发指,这类人无疑是非常懂动态代理这点小伎俩的)做了一两年新人来说,总是摸不 ...

  3. easyui设置界面的高度自适应

    单独设置界面中特定region区时,高度自适应失败:设置整个界面时成功.实现代码在script块中,具体代码如下: <%@ Page Language="C#" AutoEv ...

  4. 两句话概括“sql外键”

    外键的使用就是: 1.外键表可以删除,外键表删完了 才能删主键表2.添加的时候不能添加在主键没有的内容

  5. TreeView 节点的显示,读取,操作

    using System;using System.Collections;using System.Configuration;using System.Data;using System.Linq ...

  6. c++map的用法 分类: POJ 2015-06-19 18:36 11人阅读 评论(0) 收藏

    c++map的用法 分类: 资料 2012-11-14 21:26 10573人阅读 评论(0) 收藏 举报 最全的c++map的用法 此文是复制来的0.0 1. map最基本的构造函数: map&l ...

  7. E: Sub-process /usr/bin/dpkg returned an error code (1) 解决方案

    转载自:http://www.cnblogs.com/eddy-he/archive/2012/06/20/2555918.html cd /var/lib/dpkg sudo mv info inf ...

  8. js调用MVC3自带js验证

    验证: if ($(this).is("form")) {                        return $(this).validate().checkForm() ...

  9. VS2010常用快捷键

    ctrl + K, ctrl + F 自动调整格式(选中内容) F12 转到定义 ctrl + F12 转到声明 ctrl + tab 文档窗口转换(下一个文档窗口) alt + F6 下一个面板窗口 ...

  10. linux下svn常用指令

    windows下的TortoiseSVN是资源管理器的一个插件,以覆盖图标表示文件状态,几乎所以命令都有图形界面支持,比较好用,这里就不多说.主要说说linux下svn的使用,因为linux下大部分的 ...