Я создаю приложение на Swift UI и Swift 6 для iOS 26. Я использую SwiftData для хранения контента. У меня есть лист редактирования, с которым у меня возникают проблемы.
Когда лист загружается, пользователь не может нажать TextField для немедленного редактирования; для появления клавиатуры требуется несколько нажатий, и поэтому текст в поле выделяется для вырезания, копирования и вставки. Я занимался этим уже несколько дней, даже использовал искусственный интеллект, чтобы посмотреть, сможет ли он помочь, и не продвинулся дальше.
Мой код EditPolicyView.swift:
Я создаю приложение на Swift UI и Swift 6 для iOS 26. Я использую SwiftData для хранения контента. У меня есть лист редактирования, с которым у меня возникают проблемы. Когда лист загружается, пользователь не может нажать TextField для немедленного редактирования; для появления клавиатуры требуется несколько нажатий, и поэтому текст в поле выделяется для вырезания, копирования и вставки. Я занимался этим уже несколько дней, даже использовал искусственный интеллект, чтобы посмотреть, сможет ли он помочь, и не продвинулся дальше. Мой код EditPolicyView.swift: [code]// // EditPolicyView.swift // Policy Pal // // Created by Justin Erswell on 09/01/2026. //
import SwiftUI import SwiftData import PhotosUI
// Lightweight attachment summary - no binary data, just metadata for display struct AttachmentSummary: Identifiable, Sendable { let id: UUID let filename: String let mimeType: String let isExisting: Bool // true = already saved in SwiftData, false = newly added
var isPDF: Bool { mimeType == "application/pdf" }
// Init for existing attachments (extracted values, not the model itself) init(id: UUID, filename: String, mimeType: String, isExisting: Bool) { self.id = id self.filename = filename self.mimeType = mimeType self.isExisting = isExisting }
// Convenience init for new attachments init(id: UUID = UUID(), filename: String, mimeType: String) { self.id = id self.filename = filename self.mimeType = mimeType self.isExisting = false } }
// Simple value struct to pass data without SwiftData observation // NOTE: Attachments are NOT copied here to avoid blocking main thread with large binary data struct EditPolicyData: Identifiable { let id: PersistentIdentifier var name: String var category: PolicyCategory var provider: String var policyNumber: String var cost: Decimal var costFrequency: CostFrequency var renewalDate: Date var notes: String var reminderThirtyDays: Bool var reminderFourteenDays: Bool var reminderThreeDays: Bool var reminderRenewalDay: Bool
init(from policy: PolicyItem) { let start = CFAbsoluteTimeGetCurrent() self.id = policy.persistentModelID print("⏱️ EditPolicyData: persistentModelID took \(CFAbsoluteTimeGetCurrent() - start)s")
// Actual form view with inline @State initialization (like AddPolicyView) struct EditPolicyFormView: View { @Environment(\.dismiss) private var dismiss @Environment(\.modelContext) private var modelContext @EnvironmentObject private var appSettings: AppSettings
// Store the policy ID for saving let policyID: PersistentIdentifier
// Initial values passed in let initialName: String let initialCategory: PolicyCategory let initialProvider: String let initialPolicyNumber: String let initialCost: Decimal let initialCostFrequency: CostFrequency let initialRenewalDate: Date let initialNotes: String let initialReminderThirtyDays: Bool let initialReminderFourteenDays: Bool let initialReminderThreeDays: Bool let initialReminderRenewalDay: Bool
// Form state - using inline initialization like AddPolicyView @State private var name = "" @State private var category: PolicyCategory = .insurance @State private var provider = "" @State private var policyNumber = "" @State private var cost: Decimal = 0 @State private var costString = "" @State private var costFrequency: CostFrequency = .yearly @State private var renewalDate = Date() @State private var notes = ""
// Reminder schedule @State private var reminderThirtyDays = true @State private var reminderFourteenDays = true @State private var reminderThreeDays = true @State private var reminderRenewalDay = true
// Track if we've loaded initial values @State private var hasLoadedInitialValues = false
// Attachments - use lightweight summaries for display, track changes separately @State private var attachmentSummaries: [AttachmentSummary] = [] @State private var newAttachments: [Attachment] = [] // Newly added attachments (with data) @State private var deletedAttachmentIDs: Set = [] // IDs of existing attachments to delete @State private var attachmentsLoaded = false @State private var selectedPhotoItems: [PhotosPickerItem] = [] @State private var showingDocumentScanner = false @State private var showingFilePicker = false
@State private var showingValidationError = false @State private var validationErrorMessage = ""
private var reminderFooterText: String { isSubscription ? "You'll receive notifications at 9:00 AM before your billing date." : "You'll receive notifications at 9:00 AM on these days." }
// Load attachment METADATA only (not binary data) to avoid blocking main thread private func loadAttachments() async { guard !attachmentsLoaded else { return } let start = CFAbsoluteTimeGetCurrent() print("⏱️ loadAttachments: starting...")
// Use a background context to avoid blocking main thread let container = modelContext.container let policyIDCopy = policyID
// Fetch raw metadata as tuples (Sendable) from background let metadata: [(UUID, String, String)] = await Task.detached { let bgStart = CFAbsoluteTimeGetCurrent() let backgroundContext = ModelContext(container) guard let policy = backgroundContext.model(for: policyIDCopy) as? PolicyItem else { return [] } // Only access metadata properties, NOT the data property let result = policy.safeAttachments.map { ($0.id, $0.filename, $0.mimeType) } print("⏱️ loadAttachments background task took \(CFAbsoluteTimeGetCurrent() - bgStart)s") return result }.value
// Create summaries on main actor attachmentSummaries = metadata.map { AttachmentSummary(id: $0.0, filename: $0.1, mimeType: $0.2, isExisting: true) } attachmentsLoaded = true print("⏱️ loadAttachments: TOTAL took \(CFAbsoluteTimeGetCurrent() - start)s") }
// MARK: - Save Changes private func saveChanges() { guard !name.trimmingCharacters(in: .whitespaces).isEmpty else { validationErrorMessage = "Please enter a name." showingValidationError = true return }
// Fetch the policy by ID guard let policy = modelContext.model(for: policyID) as? PolicyItem else { validationErrorMessage = "Could not find record to update." showingValidationError = true return }
// MARK: - Attachment Handling private func removeAttachment(_ summary: AttachmentSummary) { attachmentSummaries.removeAll { $0.id == summary.id } if summary.isExisting { // Mark existing attachment for deletion on save deletedAttachmentIDs.insert(summary.id) } else { // Remove newly added attachment newAttachments.removeAll { $0.id == summary.id } } }
private func processScannedImages(_ images: [UIImage]) { for (index, image) in images.enumerated() { if let data = image.jpegData(compressionQuality: 0.8) { let id = UUID() let filename = "scan_\(attachmentSummaries.count + index + 1).jpg" let mimeType = "image/jpeg"
// Add to newAttachments (with data) for saving let attachment = Attachment(filename: filename, data: data, mimeType: mimeType) attachment.id = id newAttachments.append(attachment)
private func processSelectedPhotos(_ items: [PhotosPickerItem]) { for item in items { Task { if let data = try? await item.loadTransferable(type: Data.self) { await MainActor.run { let id = UUID() let filename = "photo_\(attachmentSummaries.count + 1).jpg" let mimeType = "image/jpeg"
// Add to newAttachments (with data) for saving let attachment = Attachment(filename: filename, data: data, mimeType: mimeType) attachment.id = id newAttachments.append(attachment)
if let data = try? Data(contentsOf: url) { let id = UUID() let filename = url.lastPathComponent let mimeType = url.pathExtension.lowercased() == "pdf" ? "application/pdf" : "image/jpeg"
// Add to newAttachments (with data) for saving let attachment = Attachment(filename: filename, data: data, mimeType: mimeType) attachment.id = id newAttachments.append(attachment)