πΌοΈ Image Playground
β Difficulty: ββ
β±οΈ Est. Time: 1h
π Graphics & Media
Apple Intelligence-based AI image generation framework
iOS 18+Apple Intelligence
β¨ Image Playground?
Image Playground is an Apple Intelligence-based image generation framework introduced in iOS 18. It creates unique images on-device by combining text prompts, concepts, and styles. It provides system UI and APIs so users can easily create and share images in Messages, Notes, social apps and more. All processing happens on-device to protect privacy.
π‘ Key Features: AI Image Generation Β· Text Prompts Β· Style Selection (Animation/Illustration/Sketch) Β· On-Device Processing Β· System UI Integration Β· Fast Generation
π¨ 1. κΈ°λ³Έ μ΄λ―Έμ§ μμ±
Generate images through the ImagePlayground system UI.
ImagePlaygroundView.swift β Basic Generation
import SwiftUI // NOTE: Image Playgroundλ iOS 18μ μλ‘μ΄ κΈ°λ₯μΌλ‘, // μμ§ κ³΅κ° APIκ° νμ λμ§ μμμ΅λλ€. // μλλ μμλλ API ν¨ν΄μ λλ€. struct ImagePlaygroundView: View { @State private var generatedImage: Image? @State private var showImageGenerator = false @State private var prompt = "" var body: some View { VStack(spacing: 20) { // μμ±λ μ΄λ―Έμ§ νμ if let generatedImage { generatedImage .resizable() .scaledToFit() .frame(maxHeight: 300) .cornerRadius(12) .shadow(radius: 5) } else { RoundedRectangle(cornerRadius: 12) .fill(Color.gray.opacity(0.2)) .frame(height: 300) .overlay { VStack { Image(systemName: "photo.on.rectangle.angled") .font(.system(size: 50)) Text("μ΄λ―Έμ§λ₯Ό μμ±ν΄λ³΄μΈμ") .font(.headline) } .foregroundStyle(.secondary) } } // ν둬ννΈ μ λ ₯ TextField("μνλ μ΄λ―Έμ§ μ€λͺ ", text: $prompt) .textFieldStyle(.roundedBorder) .padding(.horizontal) // μμ± λ²νΌ Button { showImageGenerator = true } label: { Label("μ΄λ―Έμ§ μμ±", systemImage: "wand.and.stars") .frame(maxWidth: .infinity) .padding() .background(Color.blue) .foregroundStyle(.white) .cornerRadius(12) } .padding(.horizontal) .disabled(prompt.isEmpty) } .padding() // Image Playground μνΈ νμ // .imagePlayground(isPresented: $showImageGenerator, prompt: prompt) { result in // if case .success(let image) = result { // generatedImage = Image(uiImage: image) // } // } } } // μμλλ μ΄λ―Έμ§ μμ± κ²°κ³Ό νμ enum ImageGenerationResult { case success(UIImage) case failure(Error) case cancelled }
π 2. μ€νμΌ μ ν
Generate images in various styles including Animation, Illustration, and Sketch.
StyleSelector.swift β μ€νμΌ μ ν
import SwiftUI // Image Playground μ€νμΌ μ΅μ enum ImagePlaygroundStyle: String, CaseIterable, Identifiable { case animation = "μ λλ©μ΄μ " case illustration = "μΌλ¬μ€νΈ" case sketch = "μ€μΌμΉ" var id: String { rawValue } var icon: String { switch self { case .animation: return "film" case .illustration: return "paintbrush" case .sketch: return "pencil.tip" } } var description: String { switch self { case .animation: return "3D μΊλ¦ν°μ μλκ° μλ μ λλ©μ΄μ μ€νμΌ" case .illustration: return "μμΈνκ³ λ€μ±λ‘μ΄ μΌλ¬μ€νΈλ μ΄μ " case .sketch: return "λ¨μνκ³ μμ μ μΈ μ€μΌμΉ" } } } struct StyleSelectorView: View { @State private var selectedStyle: ImagePlaygroundStyle = .animation @State private var prompt = "" @State private var generatedImage: Image? @State private var isGenerating = false var body: some View { ScrollView { VStack(spacing: 24) { // μ€νμΌ μ ν VStack(alignment: .leading, spacing: 12) { Text("μ€νμΌ μ ν") .font(.headline) HStack(spacing: 12) { ForEach(ImagePlaygroundStyle.allCases) { style in Button { selectedStyle = style } label: { VStack(spacing: 8) { Image(systemName: style.icon) .font(.title2) Text(style.rawValue) .font(.caption) } .frame(maxWidth: .infinity) .padding() .background( selectedStyle == style ? Color.blue : Color.gray.opacity(0.2) ) .foregroundStyle( selectedStyle == style ? .white : .primary ) .cornerRadius(12) } } } Text(selectedStyle.description) .font(.caption) .foregroundStyle(.secondary) } .padding(.horizontal) // ν둬ννΈ μ λ ₯ VStack(alignment: .leading, spacing: 8) { Text("μ΄λ―Έμ§ μ€λͺ ") .font(.headline) TextField("μ: μ°μ£Όλ₯Ό μ¬ννλ κ³ μμ΄", text: $prompt, axis: .vertical) .textFieldStyle(.roundedBorder) .lineLimit(3...5) } .padding(.horizontal) // μμ± λ²νΌ Button { generateImage() } label: { HStack { if isGenerating { ProgressView() .tint(.white) Text("μμ± μ€...") } else { Image(systemName: "wand.and.stars") Text("μ΄λ―Έμ§ μμ±") } } .frame(maxWidth: .infinity) .padding() .background(Color.blue) .foregroundStyle(.white) .cornerRadius(12) } .padding(.horizontal) .disabled(prompt.isEmpty || isGenerating) // μμ±λ μ΄λ―Έμ§ if let generatedImage { VStack(alignment: .leading, spacing: 8) { Text("μμ±λ μ΄λ―Έμ§") .font(.headline) generatedImage .resizable() .scaledToFit() .cornerRadius(12) .shadow(radius: 5) } .padding(.horizontal) } } .padding(.vertical) } } func generateImage() { isGenerating = true // μ€μ ꡬνμμλ Image Playground API νΈμΆ Task { try? await Task.sleep(for: .seconds(2)) isGenerating = false // generatedImage = ... } } }
π¬ 3. λ©μμ§ μ± ν΅ν©
λ©μμ§ μ±μμ μ΄λ―Έμ§λ₯Ό μμ±νκ³ κ³΅μ .
MessageExtension.swift β Messages Integration
import SwiftUI import Messages // λ©μμ§ νμ₯μμ Image Playground μ¬μ© struct MessageImageGeneratorView: View { @State private var prompt = "" @State private var generatedImage: UIImage? @State private var showGenerator = false var onImageGenerated: (UIImage) -> Void var body: some View { VStack { TextField("μ΄λ―Έμ§ μ€λͺ ", text: $prompt) .textFieldStyle(.roundedBorder) .padding() Button("μμ±νκΈ°") { showGenerator = true } .buttonStyle(.borderedProminent) .disabled(prompt.isEmpty) if let generatedImage { Image(uiImage: generatedImage) .resizable() .scaledToFit() .frame(maxHeight: 200) .cornerRadius(12) Button("λ©μμ§λ‘ μ μ‘") { onImageGenerated(generatedImage) } .buttonStyle(.borderedProminent) } } } } // λ©μμ§ μ€ν°μ»€λ‘ λ³ν extension UIImage { func toMessageSticker() -> MSSticker? { // μμ νμΌλ‘ μ μ₯ let tempURL = FileManager.default.temporaryDirectory .appendingPathComponent(UUID().uuidString) .appendingPathExtension("png") guard let data = self.pngData() else { return nil } do { try data.write(to: tempURL) return try MSSticker(contentsOfFileURL: tempURL, localizedDescription: "Generated") } catch { print("β μ€ν°μ»€ μμ± μ€ν¨: \(error)") return nil } } }
π 4. 컨μ κΈ°λ° μμ±
Combine predefined concepts (themes, places, objects) to generate images.
ConceptGenerator.swift β Concept Composition
import SwiftUI // μ΄λ―Έμ§ μμ± μ»¨μ enum ImageConcept: String, CaseIterable { // μΊλ¦ν° case cat = "κ³ μμ΄" case dog = "κ°μμ§" case astronaut = "μ°μ£ΌμΈ" case robot = "λ‘λ΄" // μ₯μ case space = "μ°μ£Ό" case beach = "ν΄λ³" case mountain = "μ°" case city = "λμ" // νλ case flying = "λ μκ°λ" case dancing = "μΆ€μΆλ" case reading = "μ± μ½λ" case playing = "λκ³ μλ" } struct ConceptBasedGeneratorView: View { @State private var selectedCharacter: ImageConcept? @State private var selectedPlace: ImageConcept? @State private var selectedActivity: ImageConcept? @State private var generatedImage: Image? let characters: [ImageConcept] = [.cat, .dog, .astronaut, .robot] let places: [ImageConcept] = [.space, .beach, .mountain, .city] let activities: [ImageConcept] = [.flying, .dancing, .reading, .playing] var combinedPrompt: String { var parts: [String] = [] if let activity = selectedActivity { parts.append(activity.rawValue) } if let character = selectedCharacter { parts.append(character.rawValue) } if let place = selectedPlace { parts.append("in \(place.rawValue)") } return parts.joined(separator: " ") } var body: some View { ScrollView { VStack(spacing: 24) { // μΊλ¦ν° μ ν conceptSection( title: "μΊλ¦ν°", concepts: characters, selected: $selectedCharacter ) // νλ μ ν conceptSection( title: "νλ", concepts: activities, selected: $selectedActivity ) // μ₯μ μ ν conceptSection( title: "μ₯μ", concepts: places, selected: $selectedPlace ) // μ‘°ν©λ ν둬ννΈ VStack(alignment: .leading, spacing: 8) { Text("μμ±λ μ΄λ―Έμ§") .font(.headline) Text(combinedPrompt.isEmpty ? "μμμ μ νν΄μ£ΌμΈμ" : combinedPrompt) .padding() .frame(maxWidth: .infinity, alignment: .leading) .background(Color.gray.opacity(0.1)) .cornerRadius(8) } .padding(.horizontal) // μμ± λ²νΌ Button { generateImageWithConcepts() } label: { Label("μ΄λ―Έμ§ μμ±", systemImage: "wand.and.stars") .frame(maxWidth: .infinity) .padding() .background(Color.blue) .foregroundStyle(.white) .cornerRadius(12) } .padding(.horizontal) .disabled(combinedPrompt.isEmpty) // κ²°κ³Ό if let generatedImage { generatedImage .resizable() .scaledToFit() .cornerRadius(12) .padding(.horizontal) } } .padding(.vertical) } } func conceptSection( title: String, concepts: [ImageConcept], selected: Binding<ImageConcept?> ) -> some View { VStack(alignment: .leading, spacing: 12) { Text(title) .font(.headline) ScrollView(.horizontal, showsIndicators: false) { HStack(spacing: 12) { ForEach(concepts, id: \.self) { concept in Button { if selected.wrappedValue == concept { selected.wrappedValue = nil } else { selected.wrappedValue = concept } } label: { Text(concept.rawValue) .padding(.horizontal, 16) .padding(.vertical, 8) .background( selected.wrappedValue == concept ? Color.blue : Color.gray.opacity(0.2) ) .foregroundStyle( selected.wrappedValue == concept ? .white : .primary ) .cornerRadius(20) } } } .padding(.horizontal) } } } func generateImageWithConcepts() { // Image Playground APIλ‘ μ΄λ―Έμ§ μμ± print("π¨ ν둬ννΈ: \(combinedPrompt)") } }
βοΈ 5. κ³ κΈ μ€μ
Configure advanced options like image size, quality, and transformations.
AdvancedSettings.swift β Advanced Settings
import SwiftUI // μ΄λ―Έμ§ μμ± μ€μ struct ImageGenerationSettings { var style: ImagePlaygroundStyle = .animation var aspectRatio: AspectRatio = .square var numberOfVariations: Int = 1 var seed: Int? = nil // μΌκ΄λ κ²°κ³Όλ₯Ό μν μλ enum AspectRatio: String, CaseIterable { case square = "1:1" case portrait = "3:4" case landscape = "4:3" } } struct AdvancedGeneratorView: View { @State private var settings = ImageGenerationSettings() @State private var prompt = "" @State private var variations: [Image] = [] var body: some View { Form { Section("ν둬ννΈ") { TextField("μ΄λ―Έμ§ μ€λͺ ", text: $prompt, axis: .vertical) .lineLimit(3...5) } Section("μ€νμΌ") { Picker("μ€νμΌ", selection: $settings.style) { ForEach(ImagePlaygroundStyle.allCases) { style in Text(style.rawValue).tag(style) } } } Section("λΉμ¨") { Picker("μ’ ν‘λΉ", selection: $settings.aspectRatio) { ForEach(ImageGenerationSettings.AspectRatio.allCases, id: \.self) { ratio in Text(ratio.rawValue).tag(ratio) } } .pickerStyle(.segmented) } Section("λ³ν") { Stepper("λ³ν κ°μ: \(settings.numberOfVariations)", value: $settings.numberOfVariations, in: 1...4) } Section { Button("μμ±νκΈ°") { generateVariations() } .frame(maxWidth: .infinity) .disabled(prompt.isEmpty) } if !variations.isEmpty { Section("κ²°κ³Ό") { ForEach(variations.indices, id: \.self) { index in VStack { variations[index] .resizable() .scaledToFit() .cornerRadius(12) HStack { Button("μ μ₯") { saveImage(at: index) } .buttonStyle(.bordered) Button("곡μ ") { shareImage(at: index) } .buttonStyle(.bordered) } } } } } } .navigationTitle("κ³ κΈ μ€μ ") } func generateVariations() { // Image Playground APIλ‘ μ¬λ¬ λ³ν μμ± print("π¨ \(settings.numberOfVariations)κ° λ³ν μμ±") } func saveImage(at index: Int) { // ν¬ν λΌμ΄λΈλ¬λ¦¬μ μ μ₯ print("πΎ μ΄λ―Έμ§ \(index) μ μ₯") } func shareImage(at index: Int) { // 곡μ μνΈ νμ print("π€ μ΄λ―Έμ§ \(index) 곡μ ") } }
π‘ HIG Guidelines
- Privacy: λͺ¨λ μ²λ¦¬λ μ¨λλ°μ΄μ€μμ μνλ¨
- Apple Intelligence μꡬ: Requires iOS 18+ and Apple Intelligence supported devices
- System UI: κ°λ₯ν κ²½μ° μμ€ν μ 곡 UI μ¬μ©
- 컨ν μ€νΈ: μ± λ§₯λ½μ λ§λ ν둬ννΈ μ 곡
- μ μκΆ: Generated images are user-owned but check commercial use restrictions
π― Practical Usage
- λ©μμ§ μ±: λν μ€ μ΄λͺ¨ν°μ½/μ€ν°μ»€ μμ±
- λ ΈνΈ μ±: λ ΈνΈμ μ½ν μΆκ°
- Social Media: κ²μλ¬Όμ© μ΄λ―Έμ§ μμ±
- ν¬λ¦¬μμ΄ν°λΈ μ±: λμμΈ μ΄μ μμ±
- Education Apps: Learning Resources μκ°ν
π Learn More
- Image Playground 곡μ λ¬Έμ (μμ )
- WWDC 2024: Apple Intelligence Session
- HIG: Image Playground (μμ )
β‘οΈ Performance Tips: Image Playground leverages the Neural Engine for fast image generation, though complex prompts may take longer. Show progress to the user during generation, and generate only while the app is in the foreground (background generation isn't supported). Generated images are stored on device with iCloud sync support.
π μ°Έκ³ : Image Playground is an Apple Intelligence feature of iOS 18 β APIs may change at official release. The above code is based on expected patterns; refer to Apple's official documentation for actual implementation.