详解iOS14 Widget 开发相关及易报错地方处理

首先了解下如何创建

Xcode -> File -> New -> Target 找到 Widget Extension

如果你的 Widget 支持用户配置属性,则需要勾选这个(例如天气组件,用户可以选择城市),不支持的话则不用勾选

了解下创建Widget后,系统给我们生成的文件内容

下面这个代码是没有勾选 Include Configuration Intent 的地方

Provider

// Provider,顾名思义为小组件提供信息得一个struct
struct Provider: TimelineProvider {
  public typealias Entry = SimpleEntry

  // 编辑屏幕时,左上角选择添加小组件时候,第一次展示小组件会走这个方法
  public func snapshot(with context: Context, completion: @escaping (SimpleEntry) -> ()) {

  }

  // 这个方法内可以进行网络请求,拿到的数据保存在对应的 entry 中,调用 completion 之后会到刷新小组件
  public func timeline(with context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
    // 例如这是一个网络请求
    Network.request { data in
      let entry = SimpleEntry(date: renderDate, data: data)
      let timeline = Timeline(entries: [entry], policy: .after(nextRequestDate))
      completion(timeline)
    }
  }
}

Entry

官方解释: A type that specifies the date to display a widget, and, optionally, indicates the current relevance of the widget's content.

// 我的理解是就是存储小组件的数据的一个东西
struct SimpleEntry: TimelineEntry {
  let date: Date
  let data: Data
}

PlacehodlerView

// 这个是一个默认视图,例如网络请求失败、发生未知错误、第一次展示小组件都会展示这个view
struct PlaceholderView : View {

}

WidgetEntryView

// 这个是我们需要布局小组件长什么样子的view
struct StaticWidgetEntryView : View {

}

主入口

@main
struct StaticWidget: Widget {
  private let kind: String = "StaticWidget"

  public var body: some WidgetConfiguration {
    StaticConfiguration(kind: kind, provider: Provider(), placeholder: PlaceholderView()) { entry in
      StaticWidgetEntryView(entry: entry)
    }
    .configurationDisplayName("My Widget")
    .description("This is an example widget.")
  }
}

支持多Widget样式

@main
struct MainWidgets: WidgetBundle {

  @WidgetBundleBuilder
  var body: some Widget {
    Widget1()
    Widget2()
  }

}

勾选 Include Configuration Intent 之后可能出错的地方

如果你的app中设置了 Class Prefix 这下面这个 ConfigurationIntent.self 则需要加上对应的前缀

例如前缀是 XY 则需要修改为 XYConfigurationIntent.self

@main
struct MainWidget: Widget {
  private let kind: String = "MainWidget"

  public var body: some WidgetConfiguration {
    IntentConfiguration(kind: kind, intent: XYConfigurationIntent.self, provider: Provider(), placeholder: PlaceholderView()) { entry in
      IntentWidgetEntryView(entry: entry)
    }
    .configurationDisplayName("My Widget")
    .description("This is an example widget.")
  }
}

处理Widget点击事件

Widget 支持三种显示方式,分别是 systemSmall 、 systemMedium 、 systemLarge
small 样式只能用 widgetUrl 处理

@ViewBuilder
var body: some View {
  ZStack {
    AvatarView(entry.character)
      .widgetURL(url)
      .foregroundColor(.white)
  }
  .background(Color.gameBackground)
}

medium 和 large 可以用 Link 或者 widgetUrl 处理,我们看到里面有四个相同的view,即左边图片,右边文字的,这个view代码如下(Link方式)

struct RecipeView: View {
  let recipe: RecipeModel

  var body: some View {
    Link(destination: URL(string: "你的网址")!) {
      HStack {
        WebImageView(imageUrl: recipe.squareImageUrl)
          .frame(width: 65, height: 65)

        Text(recipe.adjName + recipe.name)
          .font(.footnote)
          .bold()
          .foregroundColor(.black)
          .lineLimit(3)
      }
    }
  }
}

添加 Link 或 widgetUrl 后,点击每个 RecipeView 都会触发事件,这时候你需要在主项目中的 AppDelegate 中的如下方法进行处理

func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {

}

关于Widget中加载网络图片的时机

当我们在func timeline(withcompletion)这个方法中请求到数据拿到图片链接后,必须同步把图片解析出来,否则直接让对应的WidgetView去load url 是加载不出来的

正确的写法

Struct Model {
 ...
 let image: UIImage
}

func timeline(with context: Context, completion: @escaping (Timeline<LFPlanEntry>) -> ()) {
  Network.request { data in
    // 解析图片
    var image: UIImage? = nil
    if let imageData = try? Data(contentsOf: url) {
      image = UIImage(data: imageData)
    }
    let model = Model(image: image ?? defalutImage) // 这里给个默认图片
    let entry = SimpleEntry(date: entryDate, data: model)
    let timeline = Timeline(entries: [entry], policy: .atEnd)
    completion(timeline)
  }
}

Struct WidgetView: View {

  let model: Model

  @ViewBuilder
  var body: some View {
    Image(uiImage: model.image)
          .resizable()
  }

}

错误的写法(直接丢url给view去加载)

struct WidgetView : View {

  let model: Model

  @State private var remoteImage : UIImage? = nil

  let defaultImage = UIImage(named: "default")!

  var body: some View {
    Image(uiImage: self.remoteImage ?? defaultImage)
      .onAppear(perform: fetchRemoteImage)
  }

  func fetchRemoteImage() {
    guard let url = URL(string: model.url) else { return }
    URLSession.shared.dataTask(with: url){ (data, response, error) in
      if let image = UIImage(data: data!){
        self.remoteImage = image
      } else {
        print(error ?? "")
      }
    }.resume()
  }
}

基于我们的app做出来的简单效果图

Widget相关资料

Widgets

Creating a Widget Extension

Keeping a Widget Up To Date

Making a Configurable Widget

到此这篇关于详解iOS14 Widget 开发相关及易报错地方处理的文章就介绍到这了,更多相关iOS14 Widget开发内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

时间: 2020-10-11

iOS开发 widget构建详解及实现代码

iOS开发之widget实现 前言     iOS extension的出现,方便了用户查看应用的服务,比如用户可以在Today的widgets中查看应用的简略信息,然后点击进入相关的应用界面. 暂且不表网络上现有的widget文章,本篇文章主要说明本人具体实现widget的步骤,希望能够帮助到需要实现widget的同行朋友. 图1 Today的widget展示----以支付宝为例说明 文章将依次从以下几个问题着手,进行详细说明: 1.如何为现有的工程添加widget: 2.如何绘制UI: 3.

iOS10 widget实现3Dtouch 弹出菜单

文章将依次从以下几个问题着手,进行详细说明: 1.如何为现有的工程添加widget: 2.如何绘制UI: 3.如何调起app: 4.如何与host app共享数据. 图2 添加today的target 图3 添加today之后的工程目录 这是添加Today Extension之后的工程目录. 到这里,为现有的工程添加Today Extension算是完成了,运行程序就可以看到类似图1的简单的效果了,很简单哈. 绘制UI 图4 删除默认创建的MainInterface并修改Info.plist 这

iOS 10 Today Widget解析

一.前言     前面一篇iOS开发之widget实现文章说到了如何在iOS 8.0-9.3上实现widget,可是iOS 10已经来袭,不了解一下iOS 10,把widget适配上去,说不过去呀!所以,本篇文章就接着说下iOS 10上面Today Widget的坑坑洼洼. 二.Today Widget新特性     安装完iOS 10的beta版本,发现苹果越发重视widget了:快速浏览,及时从喜爱的应用中了解信息,如图1所示.     现在,从锁屏页面,下拉通知栏的第一页,还有左滑主页面都

iOS开发之widget实现详解

前言     iOS extension的出现,方便了用户查看应用的服务,比如用户可以在Today的widgets中查看应用的简略信息,然后点击进入相关的应用界面. 暂且不表网络上现有的widget文章,本篇文章主要说明本人具体实现widget的步骤,希望能够帮助到需要实现widget的同行朋友. 文章将依次从以下几个问题着手,进行详细说明: 1.如何为现有的工程添加widget: 2.如何绘制UI: 3.如何调起app: 4.如何与host app共享数据. 添加Today Extension

JSP 开发之Spring Security详解

JSP 开发之Spring Security详解 前言: spring Security是一个能够为基于Spring的企业应用系统提供描述性安全访问控制解决方案的安全框架.它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC(依赖注入,也称控制反转)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作. Spring Security 的前身是 Acegi Security ,是 Spring 项

前端开发之CSS原理详解

前端开发之CSS原理详解 从事Web前端开发的人都与CSS打交道很多,有的人也许不知道CSS是怎么去工作的,写出来的CSS浏览器是怎么样去解析的呢?当这个成为我们提高CSS水平的一个瓶颈时,是否应该多了解一下呢? 一.浏览器的发展与CSS 网页浏览器主要通过 HTTP 协议连接网页服务器而取得网页, HTTP 容许网页浏览器送交资料到网页服务器并且获取网页.目前最常用的 HTTP 是 HTTP/1.1,这个协议在 RFC2616 中被完整定义.HTTP/1.1 有其一套 Internet Exp

Android开发之ImageLoader使用详解

先给大家展示效果图,看看是大家想要的效果吗,如果还满意,请参考以下代码: 前言 UniversalImageLoader是用于加载图片的一个开源项目,在其项目介绍中是这么写的, •支持多线程图片加载 •提供丰富的细节配置,比如线程池大小,HTPP请求项,内存和磁盘缓存,图片显示时的参数配置等等: •提供双缓存 •支持加载过程的监听: •提供图片的个性化显示配置接口: •Widget支持(这个,个人觉得没必要写进来,不过尊重原文) 其他类似的项目也有很多,但这个作为github上著名的开源项目被广

Android编程开发之NotiFication用法详解

本文实例讲述了Android编程开发之NotiFication用法.分享给大家供大家参考,具体如下: notification就是通知的意思,安卓中指通知栏,一般用在电话,短信,邮件,闹钟铃声,在手机的状态栏上就会出现一个小图标,提示用户处理这个快讯,这时手从上方滑动状态栏就可以展开并处理这个快讯. 在帮助文档中,是这么说的, notification类表示一个持久的通知,将提交给用户使用NotificationManager.已添加的Notification.Builder,使其更容易构建通知

Android 开发之旅:详解view的几种布局方式及实践

引言 我们对Android应用程序运行原理及布局文件可谓有了比较深刻的认识和理解,并且用"Hello World!"程序来实践证明了.在继续深入Android开发之旅之前,有必要解决前两篇中没有介绍的遗留问题:View的几种布局显示方法,以后就不会在针对布局方面做过多的介绍.View的布局显示方式有下面几种:线性布局(Linear Layout).相对布局(Relative Layout).表格布局(Table Layout).网格视图(Grid View).标签布局(Tab Layo

微信小程序开发之Tabbar实例详解

微信小程序 Tabbar 1.下载微信小程序开发软件: https://mp.weixin.qq.com/debug/wxadoc/dev/devtools/download.html?t=201714 2.扫描二维码登录,在手机点击确认登录 3.新建一个项目,这里选择无APPID,如果需要填写APPID,需要到微信小程序里面注册,然后就可以获取APPID,填写项目名称,选择项目目录(注释:微信小程序不会自己创建主目录文件,所以自己先建立一个项目文件夹,然后选择存放到这个文件夹中),添加项目,就

JSP安全开发之XSS漏洞详解

前言 大家好,好男人就是我,我就是好男人,我就是-0nise.在各大漏洞举报平台,我们时常会看到XSS漏洞.那么问题来了,为何会出现这种漏洞?出现这种漏洞应该怎么修复? 正文 1.XSS?XSS?XSS是什么鬼? XSS又叫跨站脚本攻击(Cross Site Scripting),我不会告诉他原本是叫CSS的,但是为了不和我们所用的层叠样式表(Cascading Style Sheets)CSS搞混.CSS(跨站脚本攻击),CSS(层叠样式表)傻傻分不清.所以就叫XSS咯. 2.XSS的危害是什

IOS 开发之swift中手势的实例详解

IOS 开发之swift中手势的实例详解 手势操作主要包括如下几类 手势 属性 说明 点击 UITapGestureRecognizer numberOfTapsRequired:点击的次数:numberOfTouchesRequired:点击时有手指数量 设置属性 numberOfTapsRequired 可以实现单击,或双击的效果 滑动 UISwipeGestureRecognizer direction:滑动方向 direction 滑动方向分为上Up.下Down.左Left.右Right