Muy buenas, me llamo Luis y aquí les traigo este nuevo post.
Índice
Introducción
El widget existe desde hace un tiempo en el mundo del escritorio. Fue popularizado por Apple desde el primer lanzamiento de Mac OS X Tiger en 2005. Los widgets se utilizan principalmente para informar al usuario de la información más reciente de un vistazo. El primer formato del widget para iOS ha sido Today Widget. Probablemente vea esto cuando deslice el dedo hacia la izquierda de su pantalla de inicio o su Centro de notificaciones. Pero los widgets no estaban disponibles en la pantalla de inicio hasta que se anunció en la WWDC 2020 para iOS 14, mientras que Android ha tenido Widget en la pantalla de inicio desde el lanzamiento de Android en 2007.
Para esta publicación, crearemos un widget de acertijo simple a partir de un proyecto existente.
Requisito previo
– iOS 14
– Xcode 12
¿Cómo funciona el widget?
El widget funciona como una extensión de su aplicación. No puede funcionar como una aplicación independiente. Los widgets están disponibles en tres tamaños (pequeño, mediano, grande) y pueden ser estáticos o configurables. Un widget es limitado en términos de interacción. No se puede desplazar, solo pulsar. Un widget pequeño solo puede tener un tipo de área de interacción, mientras que los widgets medianos y grandes pueden tener múltiples áreas de interacción que se pueden tocar. Un widget solo se puede escribir con SwiftUI. Arquitectura
Veamos la arquitectura de una aplicación con WidgetKit. La aplicación es básicamente su proyecto UIKit o SwiftUI. El widget funciona como una extensión fuera de su aplicación. Puede mostrarse en la pantalla de inicio de iOS / iPadOS o en el Centro de notificaciones en macOS.
La lógica compartida podrían ser algunos archivos compartidos entre dos membresías de destino o su marco compartido.
Recomiendo usar un marco compartido que contenga código reutilizable. También ayuda a su base de código a tener un problema de responsabilidad.
Antes de construir el primer widget, usaremos una aplicación existente que no tiene WidgetKit en el origen. Clone el proyecto de este repositorio git. Entonces abre RiddleProject.xcworkspace.
El proyecto debe contener todo lo que necesitamos para más adelante.
Crear un nuevo destino de widget
Para crear un nuevo objetivo, presione el botón + debajo de la membresía objetivo.
Luego seleccione Extensión de widget entonces presione próximo.
Entrar a nombre del producto, aquí usaremos RiddleWidget. Luego desmarque Incluir intención de configuración ya que no queremos hacer un widget configurable y presionar Terminar.
En la siguiente pantalla, tendrá un diálogo para activar el esquema «RiddleWidgetExtension»? Confirme presionando Activar para que podamos usar para depurar nuestro widget.
Comprender el archivo de plantilla del widget
Abierto RiddleWidget.swift
Proveedor de línea de tiempo
TimelineProvider es una pieza importante del widget. Proporciona a iOS toda la información de una futura actualización de un widget.
public func placeholder(in context: Self.Context) -> Self.Entry
Este método de marcador de posición se llama cuando se muestra el widget por primera vez que podría estar usando el modificador redacted
. Recomendaría que este método proporcione algunos datos ficticios para poder obtener una vista previa rápidamente.
public func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ())
Este método de instantánea se llama principalmente cuando el widget se encuentra en un estado transitorio, como cuando un usuario está agregando un widget. Si el widget está en modo de vista previa, recomendaría usar algunos datos ficticios para que el usuario pueda obtener una vista previa de cómo se verá si lo agregan a su pantalla de inicio.
WidgetKit proporciona al parámetro un contexto, para que pueda saber si el widget tiene una vista previa de la galería, el tamaño de la familia del widget, el tamaño real del widget.
La finalización le dice al sistema que terminó su tarea asincrónica.
public func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ())
Este método se llama la mayoría de las veces cuando el widget se muestra en la pantalla de inicio, necesita devolver una línea de tiempo de entradas. Es este método el que puede decirle a iOS cuándo es apropiado actualizar un widget. Por ejemplo, una aplicación meteorológica, puede actualizar una vez cada hora, pero para un estado de Stock o Mass Metropolitan, es posible que desee actualizar con más frecuencia.
El objeto de la línea de tiempo que debe devolver al finalizar toma dos parámetros. Las entradas de la línea de tiempo y la política. La política sobre cómo la actualización del widget puede ser nunca, después de la última fecha de entrada de la línea de tiempo o después de una hora específica.
SimpleEntry
SimpleEntry es solo un objeto simple que se ajusta al protocolo de TimelineEntry. La fecha es importante, ya que le dirá a iOS cuando necesite actualizar el widget. Probablemente agregará como propiedad adicional su modelo para la vista para que pueda vincular fácilmente los datos a su widget.
PlaceholderView (Xcode 12 Beta)
Simplemente ignore este objeto. El parámetro de marcador de posición en StaticConfiguration o IntentConfiguration está en desuso desde Xcode 12 Beta 3. Por lo tanto, es probable que WidgetKit no lo utilice.
RiddleWidgetEntryView
EntryView es solo su vista SwitUI de su widget que se muestra en su pantalla de inicio con el uso actual de la entrada de la línea de tiempo como parámetro.
RiddleWidget
RiddleWidget se ajusta a un Widget de protocolo. Se requería tener un cuerpo de algún tipo de configuración de widget.
Hay dos tipos de configuración de widgets que proporciona el SDK de Apple. Aquí usaremos un Configuración estática ya que no queremos tener un widget configurable.
Si quieres tener un widget configurable por el usuario. Necesitará crear un IntentConfiguration sino también una intención de Siri. No cubriremos aquí ya que queremos construir un widget simple.
Agregar la biblioteca compartida al widget
La primera parte es agregar una biblioteca compartida a nuestra extensión de widget para que podamos reutilizar nuestro modelo y colores.
En primer lugar, asegúrese de elegir el objetivo correcto seleccionando RiddleWidgetExtension, luego presione el botón + debajo de Framework y bibliotecas.
Luego seleccione CommonLibrary y presione Añadir.
Ahora, debería estar listo para usar el mismo modelo y paletas de colores de la biblioteca común entre la aplicación principal y la extensión del widget.
Actualizar la interfaz de usuario
Creemos este widget. Antes de actualizar nuestro widget, necesitamos importar nuestra biblioteca compartida en la parte superior de struct Provider: TimelineProvider
escribiendo import CommonLibrary
.
struct RiddleWidgetEntryView : View { var entry: Provider.Entry var body: some View { ZStack { Color(.primaryBackground) HStack { Text("🤔") .unredacted() .font(.system(size: 50)) Text("Waiting to get some riddle inserted here from a model") .foregroundColor(Color(.primaryColor)) }.padding() } } }
Entonces ahora, la primera parte será actualizar la propiedad del cuerpo de RiddleWidgetEntryView. No usaremos el modelo por el momento. Usaremos un texto estático y debería poder obtener una vista previa. También agregaremos modificador .unredacted()
por lo que no se transformará como vista de marcador de posición durante un estado de carga, por ejemplo.
struct RiddleWidget_Previews: PreviewProvider { static var previews: some View { Group { RiddleWidgetEntryView(entry: SimpleEntry(date: Date())) .previewContext(WidgetPreviewContext(family: .systemMedium)) RiddleWidgetEntryView(entry: SimpleEntry(date: Date())) .redacted(reason: .placeholder) .previewContext(WidgetPreviewContext(family: .systemMedium)) } } }
Actualice RiddleProjectPreview para que podamos obtener una vista previa del widget en el modo de marcador de posición y en el modo normal.
Por lo tanto, debería poder ver el widget en dos estados. Eso es fantástico, ¿verdad?
Actualizar el modelo SimpleEntry
struct SimpleEntry: TimelineEntry { public let date: Date public let riddle: Riddle }
Para recuperar el acertijo, necesitamos actualizar el objeto de estructura y agregar una propiedad almacenada adicional para el acertijo. Xcode debe quejarse de que falta un parámetro de argumento en la instantánea, los métodos de línea de tiempo del proveedor y los métodos de vista previa.
struct Provider: TimelineProvider { public typealias Entry = SimpleEntry public func placeholder(in context: Context) -> SimpleEntry { SimpleEntry(date: Date(), riddle: Riddle.list.first!) } public func getDnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) { let entry = SimpleEntry(date: Date(), riddle: Riddle.random()) completion(entry) } public func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) { var entries: [SimpleEntry] = [] // Generate a timeline consisting of five entries an hour apart, starting from the current date. let riddles = Riddle.shuffleList() let currentDate = Date() for hourOffset in 0 ..< 5 { let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)! let entry = SimpleEntry(date: entryDate, riddle: riddles[hourOffset]) entries.append(entry) } let timeline = Timeline(entries: entries, policy: .atEnd) completion(timeline) } }
struct RiddleWidget_Previews: PreviewProvider { static var riddle = Riddle.random() static var previews: some View { Group { RiddleWidgetEntryView(entry: SimpleEntry(date: Date(), riddle: riddle)) .previewContext(WidgetPreviewContext(family: .systemMedium)) RiddleWidgetEntryView(entry: SimpleEntry(date: Date(), riddle: riddle)) .redacted(reason: .placeholder) .previewContext(WidgetPreviewContext(family: .systemMedium)) } } }
Vincular la interfaz de usuario con la entrada
Asegurémonos de que RiddleWidgetEntryView pueda vincular el valor del objeto de entrada que se le pasó.
struct RiddleWidgetEntryView : View { var entry: Provider.Entry var body: some View { ZStack { Color(.primaryBackground) HStack { Text("🤔") .unredacted() .font(.system(size: 50)) Text(entry.riddle.text) .foregroundColor(Color(.primaryColor)) }.padding() } } }
Antes de construir y ejecutar. Asegurémonos de que el widget solo esté disponible para el widget de tamaño mediano agregando un modificador adicional a nuestra configuración estática usando .supportedFamilies([.systemMedium])
. Y al mismo tiempo, podemos actualizar el Nombre para mostrar del widget y su descripción.
@main struct RiddleWidget: Widget { private let kind: String = "RiddleWidget" public var body: some WidgetConfiguration { StaticConfiguration(kind: kind, provider: Provider()) { entry in RiddleWidgetEntryView(entry: entry) } .supportedFamilies([.systemMedium]) .configurationDisplayName("Riddle of the moment") .description("Display a random riddle each hour.") } }
Ahora, cree y ejecute el widget. Debería ver un buen widget en su pantalla de inicio.
Pero si toco el widget en este momento, simplemente abre la aplicación pero no muestra el acertijo que se muestra en el widget. Pero, ¿podemos mostrar el contenido relevante?
Eso es lo que aprenderemos a continuación con Universal Links.
Manejar un enlace universal desde un widget
Hay dos formas de abrir la aplicación desde un widget. Si su widget es de tamaño mediano o grande, puede usar Link
en una parte de su vista. Funciona de manera similar a Button
donde la acción está especializada para abrir un enlace web. En nuestro caso, simplificaremos y usaremos el modificador widgetURL(url: URL)
que utilizan toda la parte del widget para abrir la aplicación.
Agreguemos el widgetURL
modificador enRiddleWidgetEntryView
para que la aplicación principal sepa si necesita manejar el enlace universal.
struct RiddleWidgetEntryView : View { var entry: Provider.Entry var body: some View { ZStack { Color(.primaryBackground) HStack { Text("🤔") .unredacted() .font(.system(size: 50)) Text(entry.riddle.text) .foregroundColor(Color(.primaryColor)) }.padding() } .widgetURL(URL(string: "riddle://\(entry.riddle.id)")) } }
Para manejar el enlace universal, su vista necesita registrar un controlador para invocar cuando la vista recibirá una URL. Usaremos el método .onOpenURL(perform: action: @escaping (URL) -> ())
Registremos nuestro controlador como el siguiente código para que podamos extraer la identificación de la URL, encontrar el acertijo relevante que coincida con la identificación y actualizar la vista de contenido.
struct ContentView: View { @State var riddle: CommonLibrary.Riddle @State var isAnswerHidden: Bool = true var body: some View { ZStack { // Some code here } .onOpenURL(perform: { url in let id = url.absoluteString.replacingOccurrences(of: "riddle://", with: "") guard let riddle = Riddle.list.first(where: { $0.id == UInt(id) }) else { return } self.updateRiddle(with: riddle) }) } // Some other code here }
Ahora construyamos y ejecutemos, toque su widget y debería poder ver el acertijo relevante en su aplicación principal.
Conclusión
Aprender y construir un widget es fácil de hacer. WidgetKit es uno de los temas candentes de esta WWDC 20. Al hacer más visible en la pantalla de inicio, el widget será más útil que antes que el antiguo widget Today. Cuando cree un widget, no traiga demasiada información e interacción. Manténgalo simple para su usuario.
Feliz codificación.
Añadir comentario