iT邦幫忙

2025 iThome 鐵人賽

DAY 21
0
生成式 AI

iOS? AI-Yes!:用 Vide Coding 加速我的 Swift 學習曲線系列 第 21

Day 21 - 【AI 專案 II】AI 夢境解析師:打造輸入介面與多頁面傳值

  • 分享至 

  • xImage
  •  

昨天,我們成功地讓 AI 化身為設計師,根據文字描述生成了專業的配色方案。這證明了透過精準的 Prompt,我們可以讓 AI 穩定地輸出結構化的資料。

今天,我們要挑戰打造一個「AI 夢境視覺化師」,讓使用者輸入一段夢境的文字描述,AI 就能為我們生成一張代表那個夢境的圖像。

  • 困惑點:我的 App 會有兩個畫面:一個是讓使用者輸入夢境的「輸入頁」,另一個是顯示 AI 生成圖片的「結果頁」。當使用者在「輸入頁」點擊「生成」按鈕後,我要如何將那段文字傳遞到「結果頁」去?在 Day 8,我們學過用 Delegate 模式來「回傳」資料,但這次是「往前傳」,這兩種情境的最佳實踐有什麼不同嗎?

  • AI 協作

    • 我的 Prompt我正在開發一個 App,有兩個 ViewController:InputViewController 和 ResultViewController。當使用者在 InputViewController 的 UITextView 輸入文字並點擊按鈕後,我需要將這段文字傳遞到 ResultViewController 並顯示出來。請問,在 iOS 開發中,這種「由前一頁傳值到下一頁」最直接、最標準的作法是什麼?請簡單說明並不需要給程式碼

    AI 的回答:

    您好!「由前一頁傳值到下一頁」(Forward Data Passing)是 iOS 開發中最基本也最常見的操作之一。相較於用 Delegate 或 Closure 處理「由後一頁回傳值」,往前傳遞資料的方式要簡單直觀得多。

    最標準且推薦的做法是「屬性注入 (Property Injection)」。

    這個方法的核心思想是:在您準備要呈現下一個 ViewController 之前,直接設定它的一個公開屬性 (Public Property)。

    操作流程比喻:

    您可以把這個過程想像成「寄送一封信」:

    1. ResultViewController (收件人):它身上有一個公開的「信箱」(一個 publicinternalString 屬性),專門用來接收信件。例如:var dreamDescription: String?
    2. InputViewController (寄件人):當它準備要寄信時 (也就是使用者點擊按鈕,準備跳轉畫面的時候),它會做三件事:
      • 建立信件 (資料):從 UITextView 中取得使用者輸入的文字。
      • 建立收件人地址 (實例化):建立 ResultViewController 的一個實例 (instance)。
      • 投遞信件 (賦值):在將畫面 push 或 present 出去之前,直接透過這個實例,將信件內容 (文字) 投遞到「收件人」的「信箱」(屬性) 裡。例如:resultVC.dreamDescription = "我夢見了..."
    3. ResultViewController (收件人):當它被呈現到畫面上時 (例如在 viewDidLoad 生命週期中),它會檢查自己的「信箱」,如果裡面有信,就拿出來閱讀 (更新到自己的 UI 上)。

    為什麼這是最佳實踐?

    • 簡單直觀:這是最直接的方式,程式碼的意圖非常清晰:「我要去下一頁,順便把這個東西帶過去」。
    • 低耦合InputViewController 只需要知道下一頁有一個可以接收 String 的屬性,但它完全不需要知道下一頁會如何使用這段文字。同樣地,ResultViewController 也不需要知道是誰把資料傳給它的,它只管處理好自己收到的資料。
    • 類型安全:因為是直接為強型別 (Strongly-typed) 的屬性賦值,所以 Swift 的編譯器會在編譯時期就幫您檢查型別是否正確,有效避免了執行期的錯誤。

    總結來說,當您需要將資料從 A 頁面傳遞到 B 頁面時,最簡單且專業的方式就是在 B 頁面宣告一個公開的屬性,然後在 A 頁面建立 B 的實例後、跳轉之前,直接為這個屬性賦值。

    • 根據AI的建議,我把詳細的實作步驟整理如下:
      1. 建立專案與畫面:建立一個新的 Xcode 專案,並打造 InputViewController 的 UI,包含一個 UITextView 和一個 UIButton
      2. 打造結果頁:建立 ResultViewController,並在上面放置一個 UITextView 和一個 UIImageView,同時定義好接收資料的屬性。
      3. 實現跳轉與傳值:在 InputViewController 中,實作按鈕點擊事件,完成「建立 ResultViewController -> 屬性賦值 -> Push 畫面」的完整流程。

步驟一:建立專案與輸入畫面 (InputViewController)

我們建立一個新的 Xcode 專案,命名為 AIDreamVisualizer。按照慣例,我們移除 Storyboard,並建立一個 InputViewController.swift 及其 xib 檔。

接著,我們打開 InputViewController.xib 來佈局 UI:

  1. 輸入框 (UITextView):從元件庫 (+) 拖曳一個 UITextView 到畫布上。這是讓使用者輸入夢境的主要區域。

  2. 生成按鈕 (UIButton):在 UITextView下方,放置一個 UIButton,標題設為「生成」。

  3. 狀態標籤 (UILabel):在按鈕下方,放置一個 UILabel,用來顯示目前的處理狀態。

  4. 約束:為所有元件設定好約束,確保它們在不同尺寸的螢幕上都能正確顯示。

  5. 連結 IBOutlet 與 IBAction:將 UITextViewUILabelUIButton 都連結到 InputViewController.swift 中。

    image

為了讓 UITextView 的使用體驗更好,我還加上了點擊空白處或工具列按鈕來收起鍵盤的功能。
(可以透過 SPM 安裝 IQKeyboardManagerSwift 來自動化處理鍵盤。安裝完成後,最重要的一步是在 AppDelegate (或 App 啟動點) import IQKeyboardManagerSwift 並加入 IQKeyboardManager.shared.enable = true 來全域啟用它,之後就無需手動撰寫收合鍵盤等功能了。)可以在Day 9 - 數據儲存:認識 RealmSwift,為之後的專案做準備參考我的SPM安裝

不過,為了徹底理解 iOS 鍵盤的處理機制,我們在這個系列中,將會先用手動的方式來完成這個功能。

檔案:InputViewController.swift

import UIKit

class InputViewController: UIViewController, UITextViewDelegate {
    
    // MARK: - IBOutlet
    @IBOutlet weak var lbStatus: UILabel!
    @IBOutlet weak var txfDream: UITextView!
    @IBOutlet weak var btnSent: UIButton!
    
    // MARK: - LifeCycle
    override func viewDidLoad() {
        super.viewDidLoad()
        title = "輸入你的夢境"
        setupKeyboardHandling()
        setupTextView()
    }
    
    // MARK: - UI Setting
    private func setupKeyboardHandling() {
        // 添加點擊手勢來收起鍵盤
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
        tapGesture.cancelsTouchesInView = false
        view.addGestureRecognizer(tapGesture)
    }
    
    private func setupTextView() {
        // 設置 TextView 的屬性
        txfDream.delegate = self
        txfDream.layer.borderColor = UIColor.systemGray4.cgColor
        txfDream.layer.borderWidth = 1.0
        txfDream.layer.cornerRadius = 8.0
        txfDream.font = UIFont.systemFont(ofSize: 16)
        
        // 添加工具欄
        let toolbar = UIToolbar()
        toolbar.sizeToFit()
        
        let doneButton = UIBarButtonItem(
            title: "完成",
            style: .done,
            target: self,
            action: #selector(dismissKeyboard)
        )
        
        let flexSpace = UIBarButtonItem(
            barButtonSystemItem: .flexibleSpace,
            target: nil,
            action: nil
        )
        
        toolbar.items = [flexSpace, doneButton]
        txfDream.inputAccessoryView = toolbar
    }
    
    @objc private func dismissKeyboard() {
        view.endEditing(true)
    }
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
}

步驟二:打造結果頁 (ResultViewController)

接下來,我們需要一個頁面來接收並顯示夢境的解析文字與生成的圖片。

  1. Controller 資料夾中,建立一個新的 Cocoa Touch Class,命名為 ResultViewController,記得勾選 Also create XIB file

  2. 打開 ResultViewController.xib,在畫布上放置一個 UITextView 用於顯示解析文字,以及一個 UIImageView 用於顯示夢境圖片,並為它們設定好約束。

  3. 打開 ResultViewController.swift,加入最關鍵的「信箱」屬性,並連結 UI 元件。

    image

檔案:ResultViewController.swift

import UIKit

class ResultViewController: UIViewController {
    
    // MARK: - IBOutlet
    @IBOutlet weak var imgvResult: UIImageView!
    @IBOutlet weak var txfResult: UITextView!
    
    // MARK: - Property
    var interpretationText: String?
    var dreamImage: UIImage? 
    
    // MARK: - LifeCycle
    override func viewDidLoad() {
        super.viewDidLoad()
        title = "夢境分析結果"
        
        // 顯示傳遞過來的文字和圖片
        txfResult.text = interpretationText
        imgvResult.image = dreamImage
    }
}

步驟三:實現跳轉與傳值

一切準備就緒,我們回到 InputViewController.swift,完成最後的「投遞」動作。

檔案:InputViewController.swift (新增 IBAction)

// ...接續 InputViewController.swift ...

    // MARK: - IBAcion
    @IBAction func btnSent(_ sender: UIButton) {
        // 取得使用者輸入的夢境描述
        guard let dreamDescription = txfDream.text, !dreamDescription.isEmpty else {
            // 可以在此處加入提示,提醒使用者輸入內容
            return
        }
        
        // 建立「結果頁」的實例
        let resultVC = ResultViewController(nibName: "ResultViewController", bundle: nil)
        
        // 將描述文字,直接賦值給結果頁的公開屬性
        resultVC.interpretationText = dreamDescription
        // 明天我們也會把 AI 生成的圖片放在這裡
        // resultVC.dreamImage = ...
        
        // 將結果頁推入導航堆疊中
        navigationController?.pushViewController(resultVC, animated: true)
    }

    // ...接續 ...
}

今天,我們成功打通了 App 內多個頁面之間的動脈。我們為 AI 夢境視覺化師鋪好了最關鍵的道路。

明天,我們將串接外部的圖像生成 AI API,將這段文字,轉化為一幅獨一無二的超現實畫作!


上一篇
Day 20 - 【AI 專案 I】AI 設計師:打造智慧配色 App
系列文
iOS? AI-Yes!:用 Vide Coding 加速我的 Swift 學習曲線21
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言