I would like to integrate the object capture API with a ML model for analysis. So, i will need to get the current frame into CG images for further process.
Thanks in advance !
SwiftUI
RSS for tagProvide views, controls, and layout structures for declaring your app's user interface using SwiftUI.
Posts under SwiftUI tag
200 Posts
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
I just wonder if it’s possible to add push notifications to an app made it Swift Playgrounds or if it always has to be exported to XCode first
Topic:
Developer Tools & Services
SubTopic:
Swift Playground
Tags:
APNS
Swift Playground
SwiftUI
User Notifications
Apologies that this is probably a simple problem.
I've started from a sample code provided by Apple and changed it quite significantly. However, I'm not able to Archive the app.
The original visionOS sample code has the same issue, so hopefully someone will be able to spot the problem:
https://developer.apple.com/documentation/visionos/creating-stereoscopic-image-in-visionos
The problems shown in the log are:
Undefined symbol: _main
Linker command failed with exit code 1 (use -v to see invocation)
The first error seems to say that there's no "main" but there is indeed a @main in the EntryPoint.swift file.
Any ideas? I have archived other apps (built from scratch) successfully, but clearly there's something different about this sample code.
Many thanks!
When creating a principal ToolbarItem in a sidebarAdaptable TabView, unwanted space appears at the top.
Here's my code:
struct ContentView: View {
var body: some View {
TabView {
Tab("Tab", systemImage: "star") {
NavigationStack {
Text("Hello, World!")
.toolbar {
ToolbarItem(placement: .principal) {
Text("↑ Space")
}
}
.navigationBarTitleDisplayMode(.inline)
}
}
}
.tabViewStyle(.sidebarAdaptable)
}
}
I would like to know how to remove this space.
Environment:
Xcode 16.2.0
iOS 18.2
I want to add a widget to my app that will display the # of pickups the user has for the day. I have the DeviceActivityReport working in the main app but can't get it to display in the widget since it is not a supported view for widgets. Is there any workaround for getting this view to the widget?
I tried converting the DeviceActivityReport view to a UI image thinking maybe that would be a way to use a widget approved view type but ImageRenderer seems to fail to render an image for the view (which is just a Text view).
To summarize my questions:
Is it possible to display a DeviceActivityReport in a widget? If so, what is the best practice?
Is converting the DeviceActivityReport view into an image and displaying that in a widget an option?
Here's my attempt to convert the DeviceActivityReport view into a UIImage:
import SwiftUI
import _DeviceActivity_SwiftUI
struct PickupsDeviceActivityReport: View {
@State private var context: DeviceActivityReport.Context = .totalActivity
@State private var renderedImage = Image(systemName: "exclamationmark.triangle")
@Environment(\.displayScale) var displayScale
var body: some View {
renderedImage
.onAppear { render() }
.onChange(of: context) {
_ in render()
}
}
@MainActor func render() {
let renderer = ImageRenderer(content: DeviceActivityReport(context))
renderer.scale = displayScale
if let uiImage = renderer.uiImage {
renderedImage = Image(uiImage: uiImage)
}
}
}
Help is appreciated. Thank you.
I want to have my own background and foreground colors for some views and I am having a bit of trouble. I cannot figure out how to remove the margins around some built-in views. One example is below. The ScrollView portion is always black or white, depending on whether I am I dark mode or not. I've added various colors and borders to see what is going on below. I've also tried adding the modifiers to the Scroll View rather than the TextEditor and it doesn't work at all. If I don't have the .frame modifier on the ScrollView, the TextEditor moves to the top of its frame for some reason. I've played with .contentMargins, .edgeInsets, etc. with no luck
How do I get the TextEditor to fill the entire ScrollView without the margin? Thanks!
import SwiftUI
struct TextEditorView: View
{ @Binding var editString: String
var numberOfLines: Int
var lineHeight: CGFloat
{ UIFont.preferredFont(forTextStyle: .body).lineHeight
}
var viewHeight: CGFloat
{ lineHeight * CGFloat(numberOfLines) + 8
}
var body: some View
{ ScrollView([.vertical], showsIndicators: true)
{ TextEditor(text: $editString)
.border(Color.red, width: 5)
.foregroundStyle(.yellow)
.background(.blue)
.frame(minHeight:viewHeight, maxHeight: viewHeight)
.scrollContentBackground(.hidden)
}
.frame(minHeight:viewHeight, maxHeight: viewHeight)
}
}
Can someone from Apple officially confirm either a delay or cancellation of this feature? 9 days before 2024 ends and Apple said “later this year” in June 2024. Apple mentioned Swift Assist even in the MacBook Pro announcement on the 30th of October 2024 as if it already exists.
Hello SwiftUI devs, I would like to remove the "More" button that appears in the top left of the screen whenever the selected tab of a tab view is in 5th position and beyond. It ruins the layout.
struct ContentView: View {
private let tabs = (1...10).map { "\($0)" }
@State private var selectedTab: String = "5"
var body: some View {
TabView(selection: $selectedTab) {
ForEach(tabs, id: \.self) { tab in
Text("Tab \(tab)")
.tabItem {
Label("Tab \(tab)", systemImage: "star")
}
.toolbar(.hidden, for: .tabBar)
}
}
}
}
At first glance, one easy fix would be to rearrange the tabs list in the ForEach loop, putting the selected tab at the first position. This does the trick BUT we lose the states of the views, which is out of the question in my use-case.
Getting rid of the tab view and handling the logic with a simple Switch paired with a state restoration mechanism using SceneStorage or SwiftData is probably possible but sounds like a white elephant compared to finding a solution to remove that "More" button.
Thank you
In a SwiftUI lab, I was asking about setting the focus state down a view hierarchy. The answer I got was to pass the focus state down the views as a binding. Conceptually, that made sense, so I moved on to other questions. But now that I am trying to implement it, I am having problems.
In the parent view, I have something like this:
@FocusState private var focusElement: UUID?
Then I am setting a property like this in the child view:
@Binding var focusedId: UUID?
When I try to create the detail view, I'm trying this:
DetailView(focusedId: $focusElement)
But this doesn't work. The error I get is:
Cannot convert value of type 'FocusState<UUID?>.Binding' to expected argument type 'Binding<UUID?>'
What is the right way to pass down the focus state to a child view so that it can update back up to the parent view?
I am trying to update from one child view, and have a TextField in a sibling view get focus.
Hello. I am building an app that shows my walk workouts and in the detail view I want to show the route I took while walking, similar to that of the Apple Fitness App. There is a problem though, I cannot seem to understand how to connect the @State property workoutLocations array that would be used to draw the route on the map with what I get from the query. The task does successfully fetches the data but then when I try to use it later in a do-catch block nothing happens. What am I missing here?
import SwiftUI
import MapKit
import HealthKit
struct DetailView: View {
@Environment(HealthKitManager.self) var healthKitManager
let workout: HKWorkout
@State private var workoutLocations: [CLLocation] = []
var body: some View {
ScrollView {
//...
}
.task {
guard let store = self.healthKitManager.healthStore else {
fatalError("healthStore is nil. App is in invalid state.")
}
let walkingObjectQuery = HKQuery.predicateForObjects(from: workout)
let routeQuery = HKAnchoredObjectQueryDescriptor(predicates: [.workoutRoute(walkingObjectQuery)], anchor: nil)
let queryResults = routeQuery.results(for: store)
let task = Task {
var workoutRouteLocations: [CLLocation] = []
for try await result in queryResults {
let routeSamples = result.addedSamples
for routeSample in routeSamples {
let routeQueryDescriptor = HKWorkoutRouteQueryDescriptor(routeSample)
let locations = routeQueryDescriptor.results(for: store)
for try await location in locations {
workoutRouteLocations.append(location)
print(workoutRouteLocations.count) // this prints out the number of locations in the sample.
}
}
}
return workoutRouteLocations
}
do {
print(try await task.value.count) // this prints nothing. Therefore if I try to update workoutLocations array from here it would do nothing as well
// workoutLocations = try await task.value therefore does nothing and the array just doesn't get populated with the results of the task
} catch {
print(error)
}
}
}
}
In macOS, I am encountering an issue where the system API fails to grant permission to open a file despite enabling the necessary Read/Write permissions within the SandBox. Could you please elucidate the reasons behind this behavior? Thanks!
func finderOpenFileSystem(at path: String) {
let fileURL = URL(fileURLWithPath: path)
guard FileManager.default.fileExists(atPath: path) else {
print("Error: File does not exist at path: \(path)")
return
}
let success = NSWorkspace.shared.open(fileURL)
if success {
print("File opened successfully: \(fileURL)")
} else {
print("Error: Failed to open file: \(fileURL)")
}
}
In visionOS, there are existing modifiers that can completely conceal the hands. However, I am interested in learning how to achieve the effect of only one hand disappearing while the other hand remains visible.
.upperLimbVisibility(.hidden)
Hello, New to swift ui and xcode. I am building a mobil app that will need bluetooth capabilities. When I make my custom info.plist file and set "Generate info.plist file" to No, it states it cannot find my file. Then when I set that to "Yes" it gives me an error stating that there are multiple versions of the file. In my editor I cant seem to set a path to it either. Any help would be greatly appreciated.
this is the result when the the section is set to No
Cannot code sign because the target does not have an Info.plist file and one is not being generated automatically. Apply an Info.plist file to the target using the INFOPLIST_FILE build setting or generate one automatically by setting the GENERATE_INFOPLIST_FILE build setting to YES (recommended).
this is when its set to yes:
Multiple commands produce '/Users/thatcherdeyoework/Library/Developer/Xcode/DerivedData/SwiftUI-weather-etzagqgbgkjotzenbomwvkhjfhzt/Build/Products/Debug-iphoneos/SwiftUI-weather.app/Info.plist'
I get the following fatal error when the user clicks Save in AddProductionView.
Fatal error: Duplicate keys of type 'AnyHashable' were found in a Dictionary. This usually means either that the type violates Hashable's requirements, or that members of such a dictionary were mutated after insertion.
As far as I’m aware, SwiftData automatically makes its models conform to Hashable, so this shouldn’t be a problem.
I think it has something to do with the picker, but for the life of me I can’t see what.
This error occurs about 75% of the time when Save is clicked.
I'm using Xcode 16.2 and iPhone SE 2nd Gen. Any help would be greatly appreciated…
Here is my code:
import SwiftUI
import SwiftData
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.modelContainer(for: Character.self, isAutosaveEnabled: false)
}
}
}
@Model
final class Character {
var name: String
var production: Production
var myCharacter: Bool
init(name: String, production: Production, myCharacter: Bool = false) {
self.name = name
self.production = production
self.myCharacter = myCharacter
}
}
@Model
final class Production {
var name: String
init(name: String) {
self.name = name
}
}
struct ContentView: View {
@State private var showingSheet = false
var body: some View {
Button("Add", systemImage: "plus") {
showingSheet.toggle()
}
.sheet(isPresented: $showingSheet) {
AddProductionView()
}
}
}
struct AddProductionView: View {
@Environment(\.dismiss) private var dismiss
@Environment(\.modelContext) var modelContext
@State var production = Production(name: "")
@Query var characters: [Character]
@State private var characterName: String = ""
@State private var selectedCharacter: Character?
var filteredCharacters: [Character] {
characters.filter { $0.production == production }
}
var body: some View {
NavigationStack {
Form {
Section("Details") {
TextField("Title", text: $production.name)
}
Section("Characters") {
List(filteredCharacters) { character in
Text(character.name)
}
HStack {
TextField("Character", text: $characterName)
Button("Add") {
let newCharacter = Character(name: characterName, production: production)
modelContext.insert(newCharacter)
characterName = ""
}
.disabled(characterName.isEmpty)
}
if !filteredCharacters.isEmpty {
Picker("Select your role", selection: $selectedCharacter) {
Text("Select")
.tag(nil as Character?)
ForEach(filteredCharacters) { character in
Text(character.name)
.tag(character as Character?)
}
}
.pickerStyle(.menu)
}
}
}
.toolbar {
Button("Save") { //Fatal error: Duplicate keys of type 'AnyHashable' were found in a Dictionary. This usually means either that the type violates Hashable's requirements, or that members of such a dictionary were mutated after insertion.
if let selectedCharacter = selectedCharacter {
selectedCharacter.myCharacter = true
}
modelContext.insert(production)
do {
try modelContext.save()
} catch {
print("Failed to save context: \(error)")
}
dismiss()
}
.disabled(production.name.isEmpty || selectedCharacter == nil)
}
}
}
}
I get this message when trying to save my Models.
CoreData: error: SQLCore dispatchRequest: exception handling request: <NSSQLSaveChangesRequestContext: 0x303034540> , I/O error for database at /var/mobile/Containers/Data/Application/726ECA8C-6C67-4BFE-89E7-AFD8A83CAA5D/Library/Application Support/default.store. SQLite error code:1, 'no such table: ZCALENDARMODEL' with userInfo of {
NSFilePath = "/var/mobile/Containers/Data/Application/726ECA8C-6C67-4BFE-89E7-AFD8A83CAA5D/Library/Application Support/default.store";
NSSQLiteErrorDomain = 1;
}
SwiftData.DefaultStore save failed with error: Error Domain=NSCocoaErrorDomain Code=256 "The file “default.store” couldn’t be opened." UserInfo={NSFilePath=/var/mobile/Containers/Data/Application/726ECA8C-6C67-4BFE-89E7-AFD8A83CAA5D/Library/Application Support/default.store, NSSQLiteErrorDomain=1}
The App has Recipes and Calendars and the user can select a Recipe for each Calendar day. The recipe should not be referenced, it should be saved by SwiftData along with the Calendar.
import SwiftUI
import SwiftData
enum CalendarSource: String, Codable {
case created
case imported
}
@Model
class CalendarModel: Identifiable, Codable {
var id: UUID = UUID()
var name: String
var startDate: Date
var endDate: Date
var recipes: [String: RecipeData] = [:]
var thumbnailData: Data?
var source: CalendarSource?
// Computed Properties
var daysBetween: Int {
let days = Calendar.current.dateComponents([.day], from: startDate.midnight, to: endDate.midnight).day ?? 0
return days + 1
}
var allDates: [Date] {
startDate.midnight.allDates(upTo: endDate.midnight)
}
var thumbnailImage: Image? {
if let data = thumbnailData, let uiImage = UIImage(data: data) {
return Image(uiImage: uiImage)
} else {
return nil
}
}
// Initializer
init(name: String, startDate: Date, endDate: Date, thumbnailData: Data? = nil, source: CalendarSource? = .created) {
self.name = name
self.startDate = startDate
self.endDate = endDate
self.thumbnailData = thumbnailData
self.source = source
}
// Convenience initializer to create a copy of an existing calendar
static func copy(from calendar: CalendarModel) -> CalendarModel {
let copiedCalendar = CalendarModel(
name: calendar.name,
startDate: calendar.startDate,
endDate: calendar.endDate,
thumbnailData: calendar.thumbnailData,
source: calendar.source
)
// Copy recipes
copiedCalendar.recipes = calendar.recipes.mapValues { $0 }
return copiedCalendar
}
// Codable Conformance
private enum CodingKeys: String, CodingKey {
case id, name, startDate, endDate, recipes, thumbnailData, source
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(UUID.self, forKey: .id)
name = try container.decode(String.self, forKey: .name)
startDate = try container.decode(Date.self, forKey: .startDate)
endDate = try container.decode(Date.self, forKey: .endDate)
recipes = try container.decode([String: RecipeData].self, forKey: .recipes)
thumbnailData = try container.decodeIfPresent(Data.self, forKey: .thumbnailData)
source = try container.decodeIfPresent(CalendarSource.self, forKey: .source)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(name, forKey: .name)
try container.encode(startDate, forKey: .startDate)
try container.encode(endDate, forKey: .endDate)
try container.encode(recipes, forKey: .recipes)
try container.encode(thumbnailData, forKey: .thumbnailData)
try container.encode(source, forKey: .source)
}
}
import SwiftUI
struct RecipeData: Codable, Identifiable {
var id: UUID = UUID()
var name: String
var ingredients: String
var steps: String
var thumbnailData: Data?
// Computed property to convert thumbnail data to a SwiftUI Image
var thumbnailImage: Image? {
if let data = thumbnailData, let uiImage = UIImage(data: data) {
return Image(uiImage: uiImage)
} else {
return nil // No image
}
}
init(recipe: RecipeModel) {
self.name = recipe.name
self.ingredients = recipe.ingredients
self.steps = recipe.steps
self.thumbnailData = recipe.thumbnailData
}
}
import SwiftUI
import SwiftData
@Model
class RecipeModel: Identifiable, Codable {
var id: UUID = UUID()
var name: String
var ingredients: String
var steps: String
var thumbnailData: Data? // Store the image data for the thumbnail
static let fallbackSymbols = ["book.pages.fill", "carrot.fill", "fork.knife", "stove.fill"]
// Computed property to convert thumbnail data to a SwiftUI Image
var thumbnailImage: Image? {
if let data = thumbnailData, let uiImage = UIImage(data: data) {
return Image(uiImage: uiImage)
} else {
return nil // No image
}
}
// MARK: - Initializer
init(name: String, ingredients: String = "", steps: String = "", thumbnailData: Data? = nil) {
self.name = name
self.ingredients = ingredients
self.steps = steps
self.thumbnailData = thumbnailData
}
// MARK: - Copy Function
func copy() -> RecipeModel {
RecipeModel(
name: self.name,
ingredients: self.ingredients,
steps: self.steps,
thumbnailData: self.thumbnailData
)
}
// MARK: - Codable Conformance
private enum CodingKeys: String, CodingKey {
case id, name, ingredients, steps, thumbnailData
}
required init(from decoder: Decoder) throws {
...
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(name, forKey: .name)
try container.encode(ingredients, forKey: .ingredients)
try container.encode(steps, forKey: .steps)
try container.encode(thumbnailData, forKey: .thumbnailData)
}
}
SwiftData delete isn't working, when I attempt to delete a model, my app crashes and I get the following error:
SwiftData/PersistentModel.swift:359: Fatal error: Cannot remove My_App.Model2 from relationship Relationship - name: model2, options: [], valueType: Model2, destination: Model2, inverseName: models3, inverseKeypath: Optional(\Model2.models3) on My_App.Model3 because an appropriate default value is not configured.
I get that it's saying I don't have a default value, but why do I need one? Isn't @Relationship .cascade automatically deleting the associated models?
And onto of that, why is the error occurring within the do block, shouldn't it be caught by the catch, and printed?
I have put together a sample project below.
import SwiftUI
import SwiftData
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.modelContainer(for: Model3.self)
}
}
}
@Model
class Model1 {
var name: String
@Relationship(deleteRule: .cascade, inverse: \Model2.model1) var models2: [Model2] = []
init(name: String) {
self.name = name
}
}
@Model
class Model2 {
var name: String
var model1: Model1
@Relationship(deleteRule: .cascade, inverse: \Model3.model2) var models3: [Model3] = []
init(name: String, model1: Model1) {
self.name = name
self.model1 = model1
}
}
@Model
class Model3 {
var name: String
var model2: Model2
init(name: String, model2: Model2) {
self.name = name
self.model2 = model2
}
}
struct ContentView: View {
@Query var models1: [Model1]
@Environment(\.modelContext) var modelContext
var body: some View {
NavigationStack {
List(models1) { model1 in
Text(model1.name)
.swipeActions {
Button("Delete", systemImage: "trash", role: .destructive) {
modelContext.delete(model1)
do {
try modelContext.save()
//SwiftData/PersistentModel.swift:359: Fatal error: Cannot remove My_App.Model2 from relationship Relationship - name: model2, options: [], valueType: Model2, destination: Model2, inverseName: models3, inverseKeypath: Optional(\Model2.models3) on My_App.Model3 because an appropriate default value is not configured.
} catch {
print(error.localizedDescription)
}
}
}
}
.toolbar {
Button("Insert", systemImage: "plus") {
modelContext.insert(Model3(name: "model3", model2: Model2(name: "model2", model1: Model1(name: "model1"))))
}
}
}
}
}
Topic:
App & System Services
SubTopic:
iCloud & Data
Tags:
Swift Student Challenge
SwiftUI
SwiftData
I want to visualize the data stored in a DataFrame using various charts (barmark, sectormark, linemark, etc.).
My questions are as follows:
Can a DataFrame be used directly within a chart? If so, could you provide a simple example?
If it cannot be used directly, what is the correct way to use it? Could you provide an example?
Thank you for your help.
Best regards.
When I run this code, and click on one of both 'currentZin' in the first screen that comes up with the view WordView, I see the content of the .preview value I used to initialize currentVerse (Verse= .preview) and not the values of the currentVerse that is in the Button action.
When I leave the WordView-sheet and click again the WordView shows the good result. I looks that on the first click the currentVerse = verse in the Button is not executed. If Ido not initialize it, it has a nil value. Can Anyone explain what happens and how to solve it.
struct HymnVerses: View {
var hymn:Hymn
@State private var currentZin: Int = 2
@State private var isLatin: Bool = true
@State private var isMasked: Bool = false
@State private var isTranslation: Bool = true
@State private var currentSentence: String = ""
@State private var showWordView: Bool = false
@State private var currentVerse: Verse = .preview
// Deze calculated property wordt op voorhand berekend.
// Hierdoor blijft de referentie naar het origineel bestaan
// wanneer ik currentVerse bereken. Daarvoor geraakte ik ze altijd kwijt.
private var filteredVerses: [Verse] {
hymn.verses.filter { $0.zin <= currentZin }
}
var body: some View {
List {
ForEach(filteredVerses) { verse in
VStack(alignment: .leading) {
Button {
currentVerse = verse
showWordView = true
} label: {
Text("\(verse.zin). \(currentSentence(for: verse))")
.font(.headline)
}
}
} .onAppear {
currentVerse = filteredVerses.first!
}
}.sheet(isPresented: $showWordView, content: {
WordView(vers: currentVerse, showWordView: $showWordView)
})
.toolbar {
ToolbarItem(placement: .bottomBar) {
Button(isLatin ? "Dutch" : "Latin") {
isLatin.toggle()
}
}
ToolbarItem(placement: .bottomBar) {
Button(isMasked ? "Unmask" : "Mask") {
isMasked.toggle()
}
}
ToolbarItem(placement: .bottomBar) {
Button("Restart") {
currentZin = 1
}
}
ToolbarItem(placement: .bottomBar) {
Button("Next") {
if currentZin < hymn.verses.count {
currentZin += 1
}
}
}
}
}
func maskLetters(in sentence: String, with mask: Character = "*") -> String {
return sentence.map { char in
if char.isLetter {
return String(mask)
} else {
return String(char)
}
}.joined()
}
private func currentSentence(for verse: Verse) -> String {
var temp: String {
return isLatin ? verse.latijn : verse.nederlands
}
if isMasked {
return maskLetters(in: temp)
}
else {
return temp
}
}
}
#Preview {
/// the navigationStack is nodig omdat anders de toolbar niet zichtbaar is met #Preview
NavigationStack {
let allTexts = AllTexts()
HymnVerses(hymn: .preview).environment(allTexts)
}
}
Hi,
I'm working with a very simple app that tries to read a coordinates card and past the data into diferent fields. The card's layout is COLUMNS from 1-10, ROWs from A-J and a two digit number for each cell. In my app, I have field for each of those cells (A1, A2...). I want that OCR to read that card and paste the info but I just cant. I have two problems. The camera won't close. It remains open until I press the button SAVE (this is not good because a user could take 3, 4, 5... pictures of the same card with, maybe, different results, and then? Which is the good one?). Then, after I press save, I can see the OCR kinda works ( the console prints all the date read) but the info is not pasted at all.
Any idea? I know is hard to know what's wrong but I've tried chatgpt and all it does... just doesn't work
This is the code from the scanview
import SwiftUI
import Vision
import VisionKit
struct ScanCardView: UIViewControllerRepresentable {
@Binding var scannedCoordinates: [String: String]
var useLettersForColumns: Bool
var numberOfColumns: Int
var numberOfRows: Int
@Environment(.presentationMode) var presentationMode
func makeUIViewController(context: Context) -> VNDocumentCameraViewController {
let scannerVC = VNDocumentCameraViewController()
scannerVC.delegate = context.coordinator
return scannerVC
}
func updateUIViewController(_ uiViewController: VNDocumentCameraViewController, context: Context) {}
func makeCoordinator() -> Coordinator {
return Coordinator(self)
}
class Coordinator: NSObject, VNDocumentCameraViewControllerDelegate {
let parent: ScanCardView
init(_ parent: ScanCardView) {
self.parent = parent
}
func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFinishWith scan: VNDocumentCameraScan) {
print("Escaneo completado, procesando imagen...")
guard scan.pageCount > 0, let image = scan.imageOfPage(at: 0).cgImage else {
print("No se pudo obtener la imagen del escaneo.")
controller.dismiss(animated: true, completion: nil)
return
}
recognizeText(from: image)
DispatchQueue.main.async {
print("Finalizando proceso OCR y cerrando la cámara.")
controller.dismiss(animated: true, completion: nil)
}
}
func documentCameraViewControllerDidCancel(_ controller: VNDocumentCameraViewController) {
print("Escaneo cancelado por el usuario.")
controller.dismiss(animated: true, completion: nil)
}
func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFailWithError error: Error) {
print("Error en el escaneo: \(error.localizedDescription)")
controller.dismiss(animated: true, completion: nil)
}
private func recognizeText(from image: CGImage) {
let request = VNRecognizeTextRequest { (request, error) in
guard let observations = request.results as? [VNRecognizedTextObservation], error == nil else {
print("Error en el reconocimiento de texto: \(String(describing: error?.localizedDescription))")
DispatchQueue.main.async {
self.parent.presentationMode.wrappedValue.dismiss()
}
return
}
let recognizedStrings = observations.compactMap { observation in
observation.topCandidates(1).first?.string
}
print("Texto reconocido: \(recognizedStrings)")
let filteredCoordinates = self.filterValidCoordinates(from: recognizedStrings)
DispatchQueue.main.async {
print("Coordenadas detectadas después de filtrar: \(filteredCoordinates)")
self.parent.scannedCoordinates = filteredCoordinates
}
}
request.recognitionLevel = .accurate
let handler = VNImageRequestHandler(cgImage: image, options: [:])
DispatchQueue.global(qos: .userInitiated).async {
do {
try handler.perform([request])
print("OCR completado y datos procesados.")
} catch {
print("Error al realizar la solicitud de OCR: \(error.localizedDescription)")
}
}
}
private func filterValidCoordinates(from strings: [String]) -> [String: String] {
var result: [String: String] = [:]
print("Texto antes de filtrar: \(strings)")
for string in strings {
let trimmedString = string.replacingOccurrences(of: " ", with: "")
if parent.useLettersForColumns {
let pattern = "^[A-J]\\d{1,2}$" // Letras de A-J seguidas de 1 o 2 dígitos
if trimmedString.range(of: pattern, options: .regularExpression) != nil {
print("Coordenada válida detectada (letras): \(trimmedString)")
result[trimmedString] = "Valor" // Asignación de prueba
}
} else {
let pattern = "^[1-9]\\d{0,1}$" // Solo números, de 1 a 99
if trimmedString.range(of: pattern, options: .regularExpression) != nil {
print("Coordenada válida detectada (números): \(trimmedString)")
result[trimmedString] = "Valor"
}
}
}
print("Coordenadas finales después de filtrar: \(result)")
return result
}
}
}
Hi,
I thought that drag drop reorder should be very easy with SwiftUI, but apparently I was wrong (unless I'm missing something). It seems to me that SwiftUI's drag-drop reorder is only easy for List, which supports .onMove modifier.
However, for UI like Grid, a horizontal ScrollView with items in a HStack, I don't see any easy approach to implement this. For example,
ScrollView(.horizontal) {
HStack {
ForEach(items) {
ItemView(item)
}
}
}
Does anyone know what's the best way to implement drag drop reorder for this horizontal scroll view?