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 的背景色,以及具体展示的内容大家按需绘制。

    1. - (void)viewDidLoad {
    2. [super viewDidLoad];
    3. // 设置 Widget 展示视图的大小
    4. self.preferredContentSize = CGSizeMake([UIScreen mainScreen].bounds.size.width, 100);
    5. self.view.backgroundColor = [UIColor yellowColor];
    6. }
  • 在 iOS8 - iOS9 中运行程序后,会发现一个问题:绘制的内容与左侧边界有一定距离(约 30px)。如何解决这个问题呢,NCWidgetProviding 协议给出了解决方案。iOS10 中不存在这个问题。

    1. // 实现 NCWidgetProviding 协议方法
    2. - (UIEdgeInsets)widgetMarginInsetsForProposedMarginInsets:(UIEdgeInsets)defaultMarginInsets {
    3. return UIEdgeInsetsMake(0, 0, 0, 0);
    4. }

2.3 调起 app

  • 因为 extension 和 containing app 是两个完全独立的进程,所以它们之间不能直接通信(不能像应用内部点击按钮,跳转到指定页面)。为了实现 Widget 调起 app,这里通过 openURL 的方式来启动 containing app。

  • 在 containing app 中设置 URL Schemes

  • 在 extension 的 ViewController 中添加如下代码。

    1. // 通过 extensionContext 借助 host app 调起 app
    2. // TodayWidget 为在 containing app 中设置的 URL Schemes
    3. [self.extensionContext openURL:[NSURL URLWithString:@"TodayWidget://"] completionHandler:^(BOOL success) {
    4. NSLog(@"open url result:%d", success);
    5. }];

2.4 数据共享

  • 通过 App Groups 提供的同一 group 内 app 共同读写区域,可以用 NSUserDefaults 和 NSFileManager 两种方式实现 extension 和 containing app 之间的数据共享。

    1. 通过 NSUserDefaults 共享数据
    • 保存数据

      1. - (void)saveDataByNSUserDefaults {
      2. NSUserDefaults *shared = [[NSUserDefaults alloc] initWithSuiteName:@"group.qianqianstudio.freeInHouse"];
      3. [shared setObject:@"asdfasdf" forKey:@"widget"];
      4. [shared synchronize];
      5. }
    • 读取数据

      1. - (NSString *)readDataFromNSUserDefaults {
      2. NSUserDefaults *shared = [[NSUserDefaults alloc] initWithSuiteName:@"group.qianqianstudio.freeInHouse"];
      3. NSString *value = [shared valueForKey:@"widget"];
      4. return value;
      5. }
    1. 通过 NSFileManager 共享数据
    • 保存数据

      1. - (BOOL)saveDataByNSFileManager {
      2. NSError * error = nil;
      3. NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.qianqianstudio.freeInHouse"];
      4. containerURL = [containerURL URLByAppendingPathComponent:@"Library/Caches/widget"];
      5. NSString *value = @"asdfasdfasf";
      6. BOOL result = [value writeToURL:containerURL atomically:YES encoding:NSUTF8StringEncoding error:&error];
      7. if (!result) {
      8. NSLog(@"%@", error);
      9. } else {
      10. NSLog(@"save value:%@ success.", value);
      11. }
      12. return result;
      13. }
    • 读取数据

      1. - (NSString *)readDataByNSFileManager {
      2. NSError *error = nil;
      3. NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.qianqianstudio.freeInHouse"];
      4. containerURL = [containerURL URLByAppendingPathComponent:@"Library/Caches/widget"];
      5. NSString *value = [NSString stringWithContentsOfURL:containerURL encoding:NSUTF8StringEncoding error:& error];
      6. return value;
      7. }

3、设置 Widget

  • 1、Widget 中展开、折叠

    • iOS10 在 NSExtensionContext 中,新添了 widgetLargestAvailableDisplayMode 属性,来确认当前 Widget 是展开还是折叠状态。所以,先在 viewWillAppear 中设置 Widget 的 mode 为展开。

      1. - (void)viewWillAppear:(BOOL)animated {
      2. [super viewWillAppear:animated];
      3. self.extensionContext.widgetLargestAvailableDisplayMode = NCWidgetDisplayModeExpanded;
      4. }
    • 然后,就是展开和折叠的处理了。在 NCWidgetProviding 协议中,新添了这个方法 widgetActiveDisplayModeDidChange。

      1. - (void)widgetActiveDisplayModeDidChange:(NCWidgetDisplayMode)activeDisplayMode withMaximumSize:(CGSize)maxSize {
      2. if (activeDisplayMode == NCWidgetDisplayModeCompact) {
      3. self.preferredContentSize = CGSizeMake([UIScreen mainScreen].bounds.size.width, 110);
      4. } else {
      5. self.preferredContentSize = CGSizeMake([UIScreen mainScreen].bounds.size.width, 300);
      6. }
      7. }
  • 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. javascript 金额格式化

    金额格式化 example: <!DOCTYPE html> <html> <head> <script src="http://code.jque ...

  2. [ERROR][org.springframework.web.context.ContextLoader][main] Context initialization failed org.sprin

    做一个SSH为基础框架的webapp小DEMO,复制了一把以前可以跑的代码,竟发现无法初始化数据源,报错如下: [ERROR][org.springframework.web.context.Cont ...

  3. phpize 动态添加 PHP 扩展的错误及解决方案

    使用phpize 动态添加 PHP 扩展是开发中经常需要做的事情,但是在 macOS 中,首次使用该功能必然会碰到一些错误,本文列出了这些错误的解决方法. 问题一: 执行 phpize 报错如下: $ ...

  4. Android TextView中 字体加粗方法

    textView.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));//加粗 textView.getPaint().setFakeBoldT ...

  5. JQuery知识快览之五—操作属性和结构

    前文提到JQuery提供了丰富的内置方法来操作对象集,本文主要介绍JQuery中的那些内置操作属性和DOM结构的方法. prop还是attr? 从JQuery1.6版开始,新增了prop方法来获取和设 ...

  6. Entity Framework 第四篇 优化SQL查询

    Expression<Func<TEntity, bool>>与Func<TEntity, bool>的异同 public IList<TEntity> ...

  7. Android JUnit Test——批量运行测试代码

    转载请注明出自天外归云的博客园:http://www.cnblogs.com/LanTianYou/ Android测试三要素 写Android测试用例有三要素,一是我们用的“安卓模拟器device” ...

  8. SharePoint API测试系列——Records.BypassLocks测试

    转载请注明出自天外归云的博客园:http://www.cnblogs.com/LanTianYou/ 对于SharePoint中已经是Record的Item,我们想要修改他的属性,这在UI界面是无法完 ...

  9. 从客户端中检测到有潜在危险的Request.Form 值

    今天往MVC中加入了一个富文本编辑框,在提交信息的时候报了如下的错误:从客户端(Content="<EM ><STRONG ><U >这是测试这...&q ...

  10. Codeforces Round #370 (Div. 2) A

    Description There are n integers b1, b2, ..., bn written in a row. For all i from 1 to n, values ai ...