You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

macOS SwiftUI应用含Image的View中TextField输入卡顿求助

macOS SwiftUI应用TextField输入卡顿问题

我们的macOS SwiftUI应用存在以下问题:当在包含Image组件的视图内操作TextField时,输入会出现卡顿——快速输入字符时,TextField的显示会滞后于用户的键入动作。

我们已经调试了代码,并用Instruments工具进行了性能分析,但仍未定位到卡顿的根源。

应用结构

  • 顶层ContentView展示人员列表
  • 点击列表中的人员,进入EditPersonView,该视图显示人员姓名(TextField组件)以及3张从照片库选择的图片
  • 卡顿仅发生在快速输入TextField的场景下

图片处理逻辑

图片数据存储在SwiftData中,我们在.onAppear生命周期方法中加载图片数据并转换为Image数组存入内存,目的是避免视图重绘时重复加载图片。

完整代码

Person模型

import SwiftUI
import SwiftData

@Model
class Person {
    var name: String
    @Attribute(.externalStorage) var photos: [Data] = []

    init(name: String, emailAddress: String, details: String, metAt: Event? = nil) {
        self.name = name
    }
}

ContentView

import SwiftUI
import SwiftData

struct ContentView: View {
    @State private var path = NavigationPath()
    @Environment(\.modelContext) var modelContext
    @State private var searchText = ""
    @State private var sortOrder = [SortDescriptor(\Person.name)]
    @Query var people: [Person]
    
    var body: some View {
        NavigationStack {
            List {
                ForEach(people) { person in
                    NavigationLink(value: person) {
                        Text(person.name)
                    }
                }
            }
            .navigationDestination(for: Person.self) { person in
                EditPersonView(person: person, navigationPath: $path)
            }
        }
    }

    func addPerson() {
        let person = Person(name: "New Person", emailAddress:"", details: "")
        modelContext.insert(person)
        path.append(person)
    }
}

#Preview {
    ContentView()
}

EditPersonView

import PhotosUI
import SwiftData
import SwiftUI

struct EditPersonView: View {
    @Bindable var person: Person
    @Binding var navigationPath: NavigationPath
    @State private var pickerItems = [PhotosPickerItem]()
    @State private var selectedImages = [Image]()

    var body: some View {
        VStack {
            ScrollView(.horizontal, showsIndicators: true) {
                HStack(spacing: 8) {
                    ForEach(0..<selectedImages.count, id: \.self) { index in
                        selectedImages[index]
                            .resizable()
                            .scaledToFit()
                    }
                }
            }
            
            HStack {
                PhotosPicker("Select images", selection: $pickerItems, matching: .images, photoLibrary: .shared())
                Button("Remove photos") {
                    person.photos.removeAll()
                    selectedImages.removeAll()
                    pickerItems.removeAll()
                }
            }

            TextField("Name", text: $person.name)
                .textContentType(.name)

        }
        .navigationTitle("Edit Person")
        .navigationDestination(for: Event.self) { event in
            //show some info about the event. unrelated to issue
        }
        .onChange(of: pickerItems, addImages)
        .padding()
        .onAppear {
            person.photos.forEach { photoData in
                let nsImage = NSImage(data: photoData)
                let image = Image(nsImage: nsImage!)
                selectedImages.append(image)
            }
        }
    }

    func addImages() {
        Task {
            selectedImages.removeAll()
            
            for item in pickerItems {
                if let loadedImageData = try await item.loadTransferable(type: Data.self) {
                    person.photos.append(loadedImageData)
                    let nsImage = NSImage(data: loadedImageData)
                    let image = Image(nsImage: nsImage!)
                    selectedImages.append(image)
                }
            }
        }
    }
}

#Preview {
    do {
        let previewer = try Previewer()

        return EditPersonView(
            person: previewer.person,
            navigationPath: .constant(NavigationPath())
        )
        .modelContainer(previewer.container)
    } catch {
        return Text("Failed to create preview: \(error.localizedDescription)")
    }
}

Previewer

import Foundation
import SwiftData
import SwiftUICore

@MainActor
struct Previewer {
    let container: ModelContainer
    let event: Event
    let person: Person
    let ship: Image
    
    init() throws {
        let config = ModelConfiguration(isStoredInMemoryOnly: true)
        container = try ModelContainer(for: Person.self, configurations: config)
        
        event = Event(name: "Preview Event", location: "Preview Location")
        person = Person(name: "Preview Name", emailAddress: "preview@preview.com", details: "", metAt: event)
        ship = Image("Enterprise") //any image for the preview
        
        container.mainContext.insert(person)
    }
}

主入口

import SwiftUI
import SwiftData

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(for: Person.self)
    }
}

卡顿现象示例

快速输入时,最后键入的字符会延迟约1秒才显示在TextField中。

环境信息

该问题在以下设备和系统版本中均可复现:

  • Mac Studio M1 Max
  • iMac M4
  • MacBook Pro M1 Pro
  • 系统版本:Sequoia 15.3.2
  • XCode版本:16.2

内容的提问来源于stack exchange,提问作者Jay

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 14:13:15