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. 收缩 虚拟硬盘 shrink vhd

    在使用WIN2012 的Hyper-v的虚拟磁盘时, 有时需要将磁盘中未使用的控件收缩掉, 这时就需要使用Hyper-v磁盘工具的收缩功能. 如果使用Hyper-v磁盘工具, 不能对vhd虚拟磁盘进行 ...

  2. PostgreSQL Hot Standby的搭建

    一. 简介:          PG在9.*版本后热备提供了新的一个功能,那就是Stream Replication的读写分离,是PG高可用性的一个典型应用.这个功能在oracle中叫active d ...

  3. python基础使用

    Python 标识符 在python里,标识符有字母.数字.下划线组成. 在python中,所有标识符可以包括英文.数字以及下划线(_),但不能以数字开头. python中的标识符是区分大小写的. 以 ...

  4. sp_sys_ERPTrigger_base

    USE [GalaxyPointDB24]GO/****** Object:  StoredProcedure [dbo].[sp_zy_Process_scrap]    Script Date: ...

  5. 新注册域名greenopen.site,向专业道路进军

    新注册域名greenopen.site,向专业道路进军,欢迎访问!届时可以通过 greenopen.site jsxyhelu.github.io 或者jsxyhelu.cnblogs.com 访问本 ...

  6. 开发与测试整体过程中的Git分支merge流程

    开发与测试整体过程中的Git分支merge流程 Git分支merge之开发流程 首先在Gitlab上有个仓库存储着原始的项目代码,其中包含一个叫master的分支.然后可能按功能进行分配,由不同的开发 ...

  7. mvc ==》json url

    mvc ==>json 这也是.net 4.0 与4.5的区别,4.0 自带 ,而4.5 则要自己加上去,微软说是 當然,最簡單的應變措施就是直接把這段設定加回去,不過,加回去之前請三思,這條設 ...

  8. flume ng之TailSource

    在它里面自带了一个TailSource以及TailDirSource,这个Source是负责读取一个文件,并一行一行的发送到sink端,而在flume-ng 1.4.0里面没有自带TailSource ...

  9. zImage.img、ramdisk.img、system.img、userdata.img介绍及解包、打包方法

    ramdisk.img system.img userdata.img介绍及解包.打包方法 Android 源码编译后,在out/target/product/generic下生成ramdisk.im ...

  10. CSocket客户端(TCP)

    首先是UDP和TCP的区别: UDP是不连接服务器,每次发送数据的时候需要服务器的IP:而TCP是先连接服务器,保持常连接,然后直接发送不需要IP. 下面是TCP客户端: 1.新建项目,TestCSo ...