Разработка
Добавляем Image Playground в приложение
Интеграция Apple Image Playground с SwiftUI или UIKit открывает новые возможности для создания динамических визуальных эффектов, интерактивного контента и продвинутых инструментов дизайна.
Помимо приложения для пользователей, разработчикам Image Playground предлагает фреймворк, позволяющий интегрировать генерацию изображений в приложения, встраивая компоненты, вызывающие и представляющие интерфейс Image Playground, доступный как в UIKit, так и в SwiftUI.
Чтобы углубиться в тему, мы реализуем приложение, генерирующее обложки для историй.
1. Начало работы
Подключаем фичу в SwiftUI
Чтобы воспользоваться преимуществами Image Playground, независимо от того, разрабатываете ли вы в UIKit или SwiftUI, импортируйте фреймворк Image Playground.
import ImagePlayground
Во-вторых, обязательно установите минимальное развертывание на 18.1 для iOS и 15.1 для macOS, чтобы избежать ошибок Xcode и иметь возможность создавать приложение только для поддерживаемых операционных систем.
Как вариант, установите атрибуты, чтобы убедиться в наличии Image Playgrounds в операционной системе перед его использованием.
Обработайте в точке входа в приложение ОС устройства:
xxxxxxxxxx
import SwiftUI
@main
struct StoryCoverGenerator: App {
var body: some Scene {
WindowGroup {
// 1. The available version
if #available(iOS 18.1, macOS 15.1, *) {
ContentView()
// 2. Fallback on earlier versions
} else {
Text("Operating System not supported")
}
}
}
}
- Убедитесь, что версия ОС не ниже iOS 18.1 или macOS 15.1.
- Обеспечьте резервное представление для устройств с меньшими версиями.
Сделайте то же самое для ContentView
, но теперь в отношении доступности функции Image Playground.
xxxxxxxxxx
import SwiftUI
import ImagePlayground
// 1. Define the OS availability
@available(iOS 18.1, macOS 15.1, *)
struct ContentView: View {
// 2. Check the availability of the feature
@Environment(\.supportsImagePlayground) private var supportsImagePlayground
var body: some View {
if supportsImagePlayground {
// Shows the UI to use the feature
} else {
Text("This device does not support Image Generation features.")
}
}
}
- Добавьте атрибут
@available
, чтобы указать компилятору доступность этой структуры в ОС. - Проверьте специальную переменную окружения для обработки отображения фичи в зависимости от типа устройства, чтобы избежать включения компонентом функции на устройствах с подходящей ОС, но не поддерживающих Apple Intelligence.
Подключаем фичу в UIKit
Давайте рассмотрим, как использовать преимущества атрибутов для обработки версии ОС и доступности функций при реализации в UIKit.
Во-первых, создайте UIViewController
для обработки представления, когда функция недоступна.
xxxxxxxxxx
import UIKit
class UnavailableViewController: UIViewController {
// Create the UI as you see fit
}
Во-вторых, создайте UIViewController
, отвечающий за генерацию обложки истории, и установите атрибуты доступности ОС.
xxxxxxxxxx
import UIKit
import ImagePlayground
// The os availability attributes
@available(iOS 18.1,macOS 15.1, *)
// The view controller responsible for the Story Cover Generation
class StoryCoverGeneratorViewController: UIViewController {
...
}
Теперь мы готовы обработать сцену, чтобы представить ее в соответствии с доступностью функции.
xxxxxxxxxx
import UIKit
import ImagePlayground
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let _ = (scene as? UIWindowScene) else { return }
// 1. The view controller
let vc: UIViewController
// 2. Check the os version and the feature availability
if #available(iOS 18.1, macOS 15.1, *), ImagePlaygroundViewController.isAvailable {
// a. ViewController for the Story Cover Generator
vc = StoryCoverGeneratorViewController()
} else {
// b. ViewController if unavailable
vc = UnavailableViewController()
}
let navigationController = UINavigationController(rootViewController: vc)
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
}
}
- Объявите переменную, в которой будет храниться контроллер представления.
- Используйте атрибут для проверки версии ОС и доступности функции с помощью
ImagePlaygroundViewController.isAvailable
. - Инициализируйте все в зависимости от того, доступна функция или нет:
- присвойте переменной экземпляр
StoryCoverGeneratorViewController
, если Image Playground доступен; - присвойте экземпляр
UnavailableViewController
, если она недоступна.
- присвойте переменной экземпляр
2. Интеграция Image Playground
Интеграция в приложение SwiftUI
Метод imagePlaygroundSheet(isPresented:concept:sourceImageURL:onCompletion:onCancellation:)
позволяет представить экран, на котором пользователь создает изображения из указанных входных данных.
Она принимает несколько параметров:
isPresented
— ожидается значение Bool, которое включает отображение представления листа, что приводит к открытию интерфейса Image Playground;concept
— строка, описывающая содержимое, в соответствии с которым будет создаваться изображение;sourceImage
ожидает URL-адрес файла в зависимости от того, должен ли промпт содержать начальную визуальную подсказку или нет. После того как эта функция открыта, ее можно переопределить непосредственно из интерфейса Image Playground. Метод позволяет сделать то же самое, используя изображение вместо URL.onCompletion
, замыкание без возвращаемого значения, которое получает сгенерированное изображение с данными параметрами:url
, URL, указывающий на временный файл изображенияonCancellation
, замыкание для обработки отмены процесса создания изображения, когда пользователь выходит из интерфейса создания без выбора изображения. После выполнения этого замыкания система автоматически закрывает экран.
xxxxxxxxxx
import SwiftUI
import ImagePlayground
@available(iOS 18.1,macOS 15.1, *)
struct ContentView: View {
// Enabling the sheet
@State var isPresented: Bool = false
var body: some View {
VStack {
// Application UI
}
// ImagePlayground Sheet Presenter
.imagePlaygroundSheet(
isPresented: $isPresented,
concept: "a red cat with blue gloves"
) { url in
// Use the URL
}
}
}
Другой метод, позволяющий описать изображение с помощью большего количества концепций, — это imagePlaygroundSheet(isPresented:concepts:sourceImageURL:onCompletion:onCancellation:)
, который требует объект [ImagePlaygroundConcept]
, коллекцию текстовых элементов, описывающих содержимое, по которым надо сгенерировать изображение.
xxxxxxxxxx
.imagePlaygroundSheet(
isPresented: $isPresented,
// Image playground concepts
concepts: [
ImagePlaygroundConcept.text("red cat with blue gloves"),
ImagePlaygroundConcept.text("big park with a pink river")
]) { url in
// Use the URL
}
Метод text(_:)
создает концепты изображения из коротких строк, описывающих содержание изображения, которое пользователь хочет сгенерировать.
Имейте в виду, что:
- Метод лучше работает с отдельными словами или короткими предложениями;
- Если строка слишком длинная, она автоматически разбивает ее на более короткие концепции, выбирая только самые важные.
Когда речь идет о более длинных строках, следует использовать метод extracted(from:title:)
, который способен выбрать несколько концептов.
xxxxxxxxxx
// ImagePlayground Sheet Presenter
.imagePlaygroundSheet(
isPresented: $isPresented,
// Image playground concepts
concepts: [
ImagePlaygroundConcept.extracted(
from: "A red cat, named Rusty, strutted through the park, with his blue gloves catching everyone’s attention. He waved at a squirrel juggling acorns and joined a duck painting by the pond. Spotting a kite tangled in a tree, Rusty climbed up, saved the kite, and became the park’s unexpected hero.",
title: "Rusty, the blue gloves cat")
]) { url in
// Use the URL
}
Этот метод возвращает объект ImagePlaygroundConcept
и принимает 2 параметра:
- Текст, длинный объект
String
, из которого выбираются концепты для игровой площадки — если его длина не является минимально необходимой для модели, строка может быть использована как есть; - Опциональный заголовок, короткая строка, представляющая текст в сжатом виде, что помогает модели извлекать из него основные понятия.
Теперь давайте объединим все в нашем представлении, чтобы сгенерировать возможные обложки на основе истории, предложенной пользователем.
xxxxxxxxxx
import SwiftUI
import ImagePlayground
@available(iOS 18.1, macOS 15.1, *)
struct ContentView: View {
// Enabling the sheet
@State var isPresented: Bool = false
var storyPlaceholder: String = "Write your story here..."
// URL of image created by AI
@State var imageURL: URL?
// Story prompt
@State var story: String = ""
// Determine the availability of the feature.
@Environment(\.supportsImagePlayground) private var supportsImagePlayground
var body: some View {
if supportsImagePlayground {
ScrollView {
VStack(alignment: .center) {
ZStack {
// Image loaded from Image Playground generation
if let url = imageURL {
AsyncImage(url: url) { image in
image.resizable()
.scaledToFill()
.frame(width: 200, height: 300)
.clipped()
.cornerRadius(8)
} placeholder: {
ProgressView()
}
} else {
ZStack(alignment: .center){
RoundedRectangle(cornerRadius: 8)
.fill(Color.red)
.frame(width: 200, height: 300)
}
if imageURL == nil {
Text("""
Story
Cover
Generator
""")
.font(.title)
.bold()
}
}
}
.shadow(color: .black.opacity(0.3), radius: 8, x: 4, y: 4)
.padding()
VStack(alignment: .leading) {
Text("Story:")
.font(.title3)
.bold()
.padding(.bottom, 5)
Text("Describe your story in no more than 250 words")
.font(.subheadline)
.bold()
.foregroundStyle(.secondary)
.padding(.bottom)
TextField(storyPlaceholder, text: $story, axis: .vertical)
.lineLimit(15)
.padding(5)
.background(Color.clear)
.overlay(
RoundedRectangle(cornerRadius: 12)
.stroke(Color.red.opacity(0.5), lineWidth: 1)
)
}
.padding()
}
.padding()
Button {
isPresented = self.checkStoryLength(story: story)
} label: {
Text("Generate")
.foregroundStyle(.red)
}
// ImagePlayground Sheet Presenter
.imagePlaygroundSheet(
isPresented: $isPresented,
concepts: [ImagePlaygroundConcept.extracted(from: story, title: nil)]) { url in
imageURL = url
}
.padding()
}
} else {
Text("This device does not support Image Generation feature.")
.foregroundStyle(.red)
}
}
// Check the lenght of Story
private func checkStoryLength(story: String) -> Bool {
if story.components(separatedBy: " ").count >= 250 {
return false
}
return true
}
}
В этом приложении пользователи могут ввести в текстовое поле рассказ длиной до 250 слов и сгенерировать изображение, соответствующее этому рассказу.
Когда пользователь нажимает кнопку Generate, приложение проверяет длину рассказа, а затем отображает интерфейс Image Playground. Условная проверка среды гарантирует, что функция доступна на поддерживаемых устройствах, а в противном случае выводит сообщение об ошибке.
Интеграция в приложение UIKit
Чтобы представить интерфейс Image Playground в UIKit, используйте ImagePlaygroundViewController
. Расширьте класс StoryCoverGeneratorViewController
новым методом, отвечающим за представление листа Image Playground.
xxxxxxxxxx
extension StoryCoverGeneratorViewController {
@IBAction
private func openImagePlayground(with story: String) {
// 1. Initialize the playground
let playground = ImagePlaygroundViewController()
// 2. Delegation
playground.delegate = self
// 2. Set extracted concepts from the story in the playground
playground.concepts = [.extracted(from: story, title: nil)]
// 3. Present the ImagePlaygroundViewController
present(playground, animated: true, completion: nil)
}
}
Метод openImagePlayground(with:)
, получающий сюжет в виде значения String, работает следующим образом:
- Инициализирует
ImagePlaygroundViewController
; - Устанавливает свойство делегата для перехвата событий жизненного цикла;
- Извлекает концепции из параметра с помощью метода
extracted(from:title:)
и сохраняет их в свойствеconceptsproperty
контроллераImagePlaygroundViewController
; - Показывает
ImagePlaygroundViewController
.
Расширьте класс StoryCoverGeneratorViewController
, чтобы он соответствовал протоколу ImagePlaygroundViewController.Delegate
.
xxxxxxxxxx
// Conforming to ImagePlaygroundViewController.Delegate
extension StoryCoverGeneratorViewController: ImagePlaygroundViewController.Delegate {
// 1. The delegate stub returning the generated image to the delegate
func imagePlaygroundViewController(_ imagePlaygroundViewController: ImagePlaygroundViewController, didCreateImageAt imageURL: URL) {
// 2. Set the image
if let image = UIImage(contentsOfFile: imageURL.path) {
imageView.image = image
} else {
print("Error loading image from URL: \(imageURL)")
}
// 3. Dismiss the sheet
dismiss(animated: true, completion: nil)
}
}
- Реализуйте метод
imagePlaygroundViewController(_:didCreateImageAt:)
, возвращающий делегату URL сгенерированного изображения; - Установите изображение, которое будет отображаться в пользовательском интерфейсе;
- Выйти из экрана Image Playground.
Интеграция в наше приложение Story Cover Generator будет выглядеть следующим образом:
xxxxxxxxxx
import UIKit
import ImagePlayground
class StoryCoverGeneratorViewController: UIViewController {
// Image Generated View that will display the generated image
private lazy var imageView: UIImageView = { ... }()
// Cover Label
private let coverLabel: UILabel = { ... }()
// Headline
private let storyLabel: UILabel = { ... }()
// Subheadline
private let instructionLabel: UILabel = { ... }()
// TextField
private let storyTextField: UITextField = { ... }()
// Generate Button
private let generateButton: UIButton = { ... }()
// Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
// Setting up the UI: add the subviews and the layout constraints
private func setupUI() { ... }
// 1. Method triggered on the button tap
@objc private func generateButtonTapped() {
// 2. Check the length of story input
if let storyText = storyTextField.text, storyText.components(separatedBy: " ").count <= 250 {
// 3. Present the Image Playground interface
openImagePlayground(with: storyText)
} else {
// 4. Present Error Alert
let alert = UIAlertController(title: "Invalid Input", message: "Please limit your story to 250 words.", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
present(alert, animated: true, completion: nil)
}
}
}
extension StoryCoverGeneratorViewController: ImagePlaygroundViewController.Delegate {
// Initialize and present the Image Playground interface.
@IBAction
private func openImagePlayground(with story: String) {
// Initialize the playground, get set up to be notified of lifecycle events.
let playground = ImagePlaygroundViewController()
playground.delegate = self
// Set extracted concepts from the story in the playground
playground.concepts = [.extracted(from: story, title: nil)]
present(playground, animated: true, completion: nil)
}
// The delegate stub returning the generated image to the delegate
func imagePlaygroundViewController(_ imagePlaygroundViewController: ImagePlaygroundViewController, didCreateImageAt imageURL: URL) {
// Add the image to the image view
if let image = UIImage(contentsOfFile: imageURL.path) {
imageView.image = image
} else {
print("Error loading image from URL: \(imageURL)")
}
dismiss(animated: true, completion: nil)
}
}
- Создайте метод, который будет срабатывать при нажатии на кнопку «Сгенерировать».
- Проверьте длину вводимого сюжета.
- Представьте интерфейс Image Playground с помощью метода
openImagePlayground(with story:)
. - Выдавайте предупреждение об ошибке, если история длиннее, чем ожидалось.
xxxxxxxxxx
import UIKit
import ImagePlayground
class StoryCoverGeneratorViewController: UIViewController {
// Properties of the view controller
// Generate Button
private let generateButton: UIButton = {
// Code that creates the button
// 1. Add the action to the target
button.addTarget(self, action: #selector(generateButtonTapped), for: .touchUpInside)
return button
}()
// Methods of the view controller
}
Добавьте этот метод в качестве действия к цели кнопки Generate.
Заключение
Интеграция Apple Image Playground с SwiftUI или UIKit открывает новые возможности для создания динамических визуальных эффектов, интерактивного контента и продвинутых инструментов дизайна. Разработчики могут легко создавать визуально насыщенный опыт, сохраняя при этом бесшовную интеграцию с разными фреймворками в своих приложениях.
На данный момент возможности ограничены, так как Apple будет расширять возможности на базе Apple Intelligence в течение года. Многообещающим является тот факт, что даже если эти возможности ограничены на данный момент, в дальнейшем они будут только улучшаться.
Если в ваших приложениях есть сценарии использования, в которых могут быть использованы возможности генерации изображений, имеет смысл начать готовиться к будущему, в котором качество результатов, создаваемых этими инструментами, будет гораздо выше.
-
Программирование3 недели назад
Конец программирования в том виде, в котором мы его знаем
-
Видео и подкасты для разработчиков6 дней назад
Как устроена мобильная архитектура. Интервью с тех. лидером юнита «Mobile Architecture» из AvitoTech
-
Магазины приложений3 недели назад
Магазин игр Aptoide запустился на iOS в Европе
-
Новости3 недели назад
Видео и подкасты о мобильной разработке 2025.8