从头开始开发gis



所需技术 (Required Technologies)

  • Xcode 12
  • Basic knowledge of Swift
  • Apple Developer account (optional)
  • iOS 14 iPhone (optional)

总览 (Overview)

We are going to create an iOS 14 home screen widget. Home screen widgets require a parent application. This walkthrough won’t go into detail about building the parent application but will focus on building a widget. Widgets are an extension of the parent application. The widget and parent application run separately but will have a shared container for data.

我们将创建一个iOS 14主屏幕小部件。 主屏幕小部件需要父应用程序。 本演练将不涉及构建父应用程序的详细信息,而是着重于构建窗口小部件。 小部件是父应用程序的扩展。 小部件和父应用程序分别运行,但是将具有共享的数据容器。

Our parent app will allow the user to select an image and add a text overlay. The widget we are building will display this image and the text overlay. Our widget will support three sizes: small, medium, and large.

我们的父应用程序将允许用户选择图像并添加文字叠加层。 我们正在构建的小部件将显示此图像和文本覆盖。 我们的小部件将支持三种尺寸:小,中和大。

Unlike the parent app, widgets are not continuously running. We will configure ours to update on ten-minute intervals.

与父应用程序不同,窗口小部件不会连续运行。 我们将配置为每隔十分钟更新一次。

入门 (Getting Started)

Create a new Xcode project:

创建一个新的Xcode项目:

  • Select “App.”
  • Add a name for your project.
  • Create.




Xcode project options Xcode项目选项

Now we will add our widget:

现在,我们将添加小部件:

  • Select the very top Xcode project file.
  • Editor → Add Target (Requires Xcode 12).
  • Select “Widget Extension.”
  • Add a name for your widget.
  • Verify that “Include Configuration Intent” is selected.
  • If prompted, select “Activate.”


Xcode project targets

Xcode项目目标

Now we are ready to get started.

现在我们准备开始。

构建父应用 (Building the Parent App)

The focus of this walkthrough is on the widget. To build the parent app for the widget, I added labels, buttons, segmented controls, text fields, and image views to the main storyboard. The final product looks similar to this:

本演练的重点是小部件。 为了构建窗口小部件的父应用程序,我在主故事板上添加了标签,按钮,分段控件,文本字段和图像视图。 最终产品看起来与此类似:


Main.Storyboard User Interface Editor

Main.Storyboard用户界面编辑器

Now that we have a parent app, we can get started on developing the widget.

现在我们有了父应用程序,我们可以开始开发小部件了。

构建小部件 (Building the Widget)

To get started, navigate to the [ProductName].swift file for your widget.

首先,导航至小部件的[ProductName].swift文件。

You should have template code similar to this:

您应该具有类似于以下内容的模板代码:


We will start in the main section. Our widget is currently set up as an IntentConfiguration. This allows the widget to have custom properties for each user. The four parameters for this configuration are:

我们将从主要部分开始。 我们的小部件当前设置为IntentConfiguration 。 这允许小部件具有每个用户的自定义属性。 此配置的四个参数是:

  • kind — This specifies our extension as a widget.
    kind —将我们的扩展名指定为小部件。
  • intent — This specifies our extension’s properties.
    intent —指定我们扩展的属性。
  • provider — This specifies how our widget will update (we will look further into this later).
    providerprovider -指定小部件的更新方式(我们将在以后对此进行进一步研究)。
  • placeholder — This specifies how our widget looks before it is configured by the user (more on this later).
    placeholder -指定我们的窗口小部件在用户配置之前的外观(稍后会详细介绍)。

The .configurationDisplayName and .description can be updated to reflect your widget.

可以更新.configurationDisplayName.description以反映您的窗口小部件。

The widget is developed using SwiftUI. The view for the widget is defined under the entry. To start developing the widget, go to the EntryView struct.

该小部件是使用SwiftUI开发的。 小部件的视图在条目下定义。 要开始开发小部件,请转到EntryView结构。

小部件系列大小(可选) (Widget Family Sizes (Optional))

The first things we want to add are the Environment and ViewBuilder definitions. This will allow us to build widgets responsive to the different widget family sizes. To do this, add the following code:

我们要添加的第一件事是EnvironmentViewBuilder定义。 这将使我们能够根据不同的窗口小部件系列大小来构建窗口小部件。 为此,请添加以下代码:


We can now get started on developing separate views for each widget size. For this project, we will use VStack (vertical stack) to build the widget view.

现在,我们可以开始为每种小部件尺寸开发单独的视图。 对于此项目,我们将使用VStack (垂直堆栈)构建窗口小部件视图。

构建小部件视图 (Building the Widget View)

Since our widget view is the same for all the sizes, I removed the switch cases and used just one VStack. We will add an Image to this stack like so:

由于我们的小部件视图对于所有大小都是相同的,因此我删除了开关盒并仅使用了一个VStack 。 我们将像这样向该堆栈添加一个Image

Note: The image data will be null.

注意:图像数据将为空。

In this example, I provide a template for adding image data to the view. Currently, no data is shared, so we are still working with null (nil) data and will see a runtime error when we run the widget. Our next challenge will be sharing data from the parent app to the widget.

在此示例中,我提供了一个用于向视图添加图像数据的模板。 当前,没有共享数据,因此我们仍在使用null( nil )数据,并且在运行窗口小部件时会看到运行时错误。 我们的下一个挑战是将父应用程序中的数据共享到小部件。

共享资料 (Sharing Data)

In the parent app, the user selects an image using the UIImagePicker. We need to be able to save this image in a way that allows the widget to also have access. The easiest way to achieve this is through user defaults.

在父应用程序中,用户使用UIImagePicker选择图像。 我们需要以一种允许小部件也可以访问的方式保存该图像。 最简单的方法是通过用户默认设置。

To allow the parent app and the widget to have access to the same user defaults, we need to create an app group:

为了允许父应用程序和小部件可以访问相同的用户默认设置,我们需要创建一个应用程序组:

  • Navigate to the Xcode project file.
  • Click the “Signing & Capabilities” tab.
  • Click on “+Capability” (top left).
  • Click “App Groups.”
  • Click the “+” to add a new group.
  • Add the group with the group.com.company.app naming convention.
    使用group.com.company.app命名约定添加组。


Setting up app group 设置应用组


Note: This will have to be done for the app and the widget.

注意:必须为应用程序和小部件完成此操作。


We can now save and access user defaults through this app group.

现在,我们可以通过此应用程序组保存和访问用户默认设置。

Saving the image:

保存图像:

Saving image to shared user defaults (parent app). 将图像保存为共享的用户默认设置(父应用程序)。

Accessing the image:

访问图像:

Accessing image from shared user defaults (widget). 从共享的用户默认设置(窗口小部件)访问图像。

User defaults work in key-value pairs. We convert the image to a generic data type, then back to an image to access it. Our widget can now access data shared from its parent app. Next, we will look at adding a text overlay to our widget.

用户默认值以键值对的形式工作。 我们将图像转换为通用数据类型,然后返回到图像以对其进行访问。 我们的小部件现在可以访问从其父应用共享的数据。 接下来,我们将看一下如何将文本叠加层添加到小部件中。

添加文字叠加层 (Adding a Text Overlay)

In the parent app, the user can specify text to overlay the image. This text is stored as a string in the user defaults. To add this text as an overlay to the widget view, we need to modify our widgetData struct to include a text overlay string. Our getData() function will also need to access the text data from the user defaults.

在父应用程序中,用户可以指定文本以覆盖图像。 此文本在用户默认值中存储为字符串。 要将此文本作为覆盖添加到窗口小部件视图中,我们需要修改widgetData结构以包含文本覆盖字符串。 我们的getData()函数还需要从用户默认值访问文本数据。


We create a new struct to build the text overlay. The overlay is added with the alignment setting the text in the lower-left corner of the widget. Now that our widget is complete, we can add a placeholder.

我们创建一个新的结构来构建文本叠加层。 在叠加层上添加了对齐方式,该对齐方式设置了小部件左下角的文本。 现在我们的小部件已完成,我们可以添加一个占位符。

添加小部件占位符 (Adding a Widget Placeholder)

The placeholder view will contain generic data to be displayed before the user-configured widget is loaded. To build the placeholder, we follow a similar process to the one above. We create our view using the VStack and add an Image view. Now we can statically set an image.

占位符视图将包含将在加载用户配置的小部件之前显示的常规数据。 要构建占位符,我们遵循与上面类似的过程。 我们使用VStack创建视图并添加一个Image视图。 现在我们可以静态设置图像。


Before we look at the provider, we will look at the SimpleEntry struct.

在查看provider之前,我们将查看SimpleEntry结构。

更新小部件数据 (Updating Widget Data)

Any data that may need updating should be defined in the SimpleEntry struct. Let’s say that we want our text overlay’s position to randomly change every ten minutes. To do this, we need to define the overlay’s position in the SimpleEntry struct:

任何可能需要更新的数据都应该在SimpleEntry结构中定义。 假设我们希望文字叠加层的位置每十分钟随机变化一次。 为此,我们需要在SimpleEntry结构中定义叠加层的位置:


We also need to update the text overlay in the widget view to use this property:

我们还需要更新小部件视图中的文本叠加层以使用此属性:


Now we can now look at the provider.

现在我们可以看看provider

小部件生命周期 (Widget Lifecycle)

The provider struct defines the widget’s lifecycle. This struct contains two parts:

provider结构定义窗口小部件的生命周期。 该结构包含两部分:

  • snapshot — The snapshot will save the widget for display in the widget gallery. It uses a new instance of SimpleEntry, so any dynamically updating content can be set.
    snapshot —快照将保存窗口小部件以显示在窗口小部件库中。 它使用SimpleEntry的新实例,因此可以设置任何动态更新的内容。
  • timeline — This will control how often the widget updates. Any properties defined in SimpleEntry will be initialized here.
    timeline -这将控制窗口小部件的更新频率。 SimpleEntry定义的任何属性SimpleEntry将在此处初始化。

We want to configure our widget to update its text overlay position every ten minutes. Modify the Provider to look similar to this:

我们想要配置我们的小部件以每十分钟更新其文本覆盖位置。 修改Provider以使其类似于以下内容:

Notice the .hour changed to .minute. 请注意,.hour更改为.minute。

We add six new SimpleEntry structs ten minutes apart, each with a random alignment position. The Timeline takes two parameters: the entries we just generated and the policy. The policy defines when we want to build a new timeline. We will leave this as .atEnd. After all the entries are used, the Timeline will be regenerated.

我们SimpleEntry十分钟添加六个新的SimpleEntry结构,每个结构具有一个随机的对齐位置。 Timeline采用两个参数:我们刚刚生成的条目和策略。 该政策定义了我们何时要建立新的时间表。 我们将其保留为.atEnd 。 使用完所有条目后,将重新生成Timeline

从父应用程序重置时间轴 (Resetting the Timeline From the Parent App)

We now have a working widget that allows a user to set an image and text overlay. The widget will now update and previews are available in the widget gallery. However, a problem exists when the user makes changes in the parent app. The widget does not know to reset its timeline to reflect these changes. To resolve this, we want to add the following line in the parent app anywhere the data is modified.

现在,我们有一个工作部件,允许用户设置图像和文本覆盖。 小部件现在将更新,并且预览可在小部件库中使用。 但是,当用户在父应用程序中进行更改时,存在一个问题。 小部件不知道要重置其时间轴以反映这些更改。 为了解决这个问题,我们想在父应用程序中在修改数据的任何地方添加以下行。

ofKind parameter takes the name of the widget ofKind参数采用小部件的名称

Any changes the user makes will now be reflected immediately.

用户所做的任何更改现在都将立即反映出来。

结语 (Wrapping Up)

Congratulations, you built a home screen widget for iOS 14. The widget is now fully functional. We started by building a view for the widget, we then set up the widget to use shared data with its parent app, we set the widget to update its text overlay view, and we can now control the widget’s lifecycle.

恭喜,您为iOS 14构建了一个主屏幕小部件。该小部件现已完全可用。 我们首先为窗口小部件构建视图,然后将窗口小部件设置为与其父应用程序使用共享数据,然后将窗口小部件设置为更新其文本覆盖视图,现在我们可以控制窗口小部件的生命周期。


从这往哪儿走 (Where to Go From Here)

Apple’s widgets give developers a lot of options to build highly customized features. Explore more complex views for your widget. Add more dynamic content to appeal to more users.

苹果的小部件为开发人员提供了许多构建高度自定义功能的选项。 探索小部件的更复杂的视图。 添加更多动态内容以吸引更多用户。

Thanks for following along. Good luck on your development journey.

感谢您的关注。 祝您发展顺利。


翻译自: https://medium.com/better-programming/how-to-develop-a-home-screen-widget-from-scratch-for-ios-14-b185972350f3

从头开始开发gis