Post

Replies

Boosts

Views

Activity

Undo/Redo with DragGesture
I have a sample macOS app that I'm working on. I can run the exactly same lines of code below for iOS. For now, I'm running code for macOS since I can just press Command + z to undo the last action. Anyway, I have two Text View objects. Since TextView has the DragGesture gesture, I am able to freely move either of them. And I want to undo and redo their positions. So the following is what I have. import SwiftUI struct ContentView: View { @State var textViews: [TextView] = [TextView(text: "George"), TextView(text: "Susan")] var body: some View { VStack { ForEach(textViews, id: \.id) { textView in textView } } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } struct TextView: View { @Environment(\.undoManager) var undoManager @StateObject private var undoModel = UndoViewModel() @State private var dragOffset: CGSize = .zero @State private var position: CGSize = .zero let id = UUID() let text: String init(text: String) { self.text = text } var body: some View { ZStack { Text(text) .fixedSize() .padding(.vertical, 10) .offset(x: dragOffset.width + position.width, y: dragOffset.height + position.height) .gesture( DragGesture() .onChanged { self.dragOffset = $0.translation } .onEnded( { (value) in self.position.width += value.translation.width self.position.height += value.translation.height self.dragOffset = .zero undoModel.registerUndo(CGSize(width: position.width, height: position.height), in: undoManager) }) ) } } } class UndoViewModel: ObservableObject { @Published var point = CGSize.zero func registerUndo(_ newValue: CGSize, in undoManager: UndoManager?) { let oldValue = point undoManager?.registerUndo(withTarget: self) { [weak undoManager] target in target.point = oldValue // registers an undo operation to revert to old text target.registerUndo(oldValue, in: undoManager) // this makes redo possible } undoManager?.setActionName("Move") point = newValue // update the actual value } } Well, if I press Command + z after moving one of them, it won't return to the last position. What am I doing wrong? Muchos thankos.
1
0
717
Sep ’23
Making a Call to a Distant Struct
Let me say that I have three structs that are sequentially connected. ContentView -> FirstView -> SecondView And I want to make a call from SecondView to ContentView with a button tap. So I have the following lines of code. import SwiftUI struct ContentView: View { @State var goToFirst = false var body: some View { NavigationStack { VStack { NavigationLink { FirstView(callBack: { sayHello() }, goToSecond: $goToFirst) } label: { Text("Go to First") } } } .navigationDestination(isPresented: $goToFirst) { } } func sayHello() { print("Hello!") } } struct FirstView: View { @State var callBack: (() -> Void)? @Binding var goToSecond: Bool var body: some View { VStack { Button("Go to Second") { goToSecond.toggle() } } .navigationDestination(isPresented: $goToSecond) { SecondView(callBack: callBack) } } } struct SecondView: View { @State var callBack: (() -> Void)? var body: some View { VStack { Button("Tap me to make a call to ContentView") { callBack?() } } } } If I tap the button in SecondView, my ContentView will receive a call and call the sayHello function. Since ContentView and SecondView are not directly connected with each other, they have to through FirstView in this case. I wonder if there's a better or easier approach in having SecondView make a call to ContentView? In UIKit and Cocoa, you can make a delegate call to a distant class even when two classes are not directly connected with other. Using the notification is another option. In SwiftUI, I suppose you don't use either of them. Muchos thankos.
1
0
708
Sep ’23
Picker with ForEach
I have a ForEach loop with Range that I use with Picker. I'm using Range because I want to set startYear and endYear when View appears. The following is my code. import SwiftUI struct ProviderCalendarView: View { @State private var startYear: Int = 2023 @State private var endYear: Int = 2034 @State private var selectedYear = 3 var body: some View { VStack { HStack { Picker(selection: $selectedYear) { ForEach((startYear...endYear), id: \.self) { year in Text("\(year)") } } label: { } } } } } And the compiler says the following. Picker: the selection "3" is invalid and does not have an associated tag, this will give undefined results. It's not a critical error. But how can I stop it? Thanks.
2
0
1.4k
Aug ’23
DisclosureGroup with Swipe Actions and Contextual Menu
I have created a simple case to make my point as follows. import SwiftUI struct ContentView: View { var body: some View { ZStack { Color.yellow.ignoresSafeArea() VStack(alignment: .leading) { ForEach(Fruit.allCases, id: \.self) { fruit in DisclosureGroup(fruit.rawValue) { VStack { Text("1") Text("2") Text("3") } } .contextMenu { Button("Hello", action: { }) } } }.padding(.horizontal, 20) } } } enum Fruit: String, CaseIterable { case apple = "Apple" case grape = "Grape" case lemon = "Lemon" case orange = "Orange" case peach = "Peach" case pineapple = "Pineapple" case watermelon = "Watermelon" } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } What I want to do is show the contextual menu when the user long-presses a fruit name, which works. Yet, if I long-press a child inside the disclosure view, I also get the contextual menu, which is unintentional. Is there a simple way by which I can stop the contextual menu to appear if long-press a child inside the disclosure view? Muchos thankos
1
0
1.1k
Aug ’23
Exporting a Document with FileDocument, Not Packaged
I'm trying to export a document file. It contains a codable struct named NoteGroup. struct NoteGroup: Codable { let id: UUID let name: String let createAt: Date let children: [NoteChild] init(id: UUID = .init(), name: String = "", createAt: Date = .init(), children: [NoteChild]) { self.id = id self.name = name self.createAt = createAt self.children = children } } , which contains another object named NoteChild. I have a FileDocument struct as follows. import SwiftUI import UniformTypeIdentifiers struct Document: FileDocument { var document: NoteGroup static var readableContentTypes = [UTType.frogType] init(document: NoteGroup = NoteGroup(children: [NoteChild(id: UUID(), name: "", createAt: Date())])) { self.document = document } init(configuration: ReadConfiguration) throws { self.init() } func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper { do { let data = try getDocumentData() let jsonFileWrapper = FileWrapper(regularFileWithContents: data) let filename = "Note.frog" jsonFileWrapper.filename = filename let fileWrapper = FileWrapper(directoryWithFileWrappers: [filename: jsonFileWrapper]) return fileWrapper } catch { throw error } } private func getDocumentData() throws -> Data { let encoder = JSONEncoder() do { let data = try encoder.encode(document) return data } catch { throw error } } } extension UTType { public static let frogType = UTType(exportedAs: "com.example.frog") } And I export a file like the following. import SwiftUI import UniformTypeIdentifiers struct ContentView: View { @State private var showingExporter = false @State var doc = Document() var body: some View { VStack { Button("Tap to export") { showingExporter.toggle() } .fileExporter( isPresented: $showingExporter, document: doc, contentType: .frogType ) { result in switch result { case .success(let file): print(file) case .failure(let error): print(error) } } }.onAppear { doc = Document(document: NoteGroup(id: UUID(), name: "Kyle", createAt: Date(), children: [NoteChild(id: UUID(), name: "Nancy", createAt: Date())])) } } } Well, I have read this topic. And I've watched this video about Uniform Type Identifiers. Thanks to the video, I am able to export a file. Yet, I end up with a folder (Frog.frog), not a packaged file. There is a JSON file in it, though. What am I doing wrong? It's for iOS. La vida no es facil. Muchos thankos.
2
0
996
Aug ’23
Removing More?
I use the ForEach enumeration to list a View horizontally. And I get the following picture. So far, so good... If I select the 5th object or 6th one, something odd (< More) appears. I don't know where it comes from. I have never seen it before. How does it happen? I wonder how I can remove it? I have searched the net for a clue to no avail. I don't even know how to describe it. The following is my code. import SwiftUI struct ContentView: View { @State var selectedTab = 0 @State var addTapped = false @State var refresh = false @State var people = [ Person(name: "Alice", systemImage: "person.circle.fill"), Person(name: "Jane", systemImage: "person.circle.fill"), Person(name: "Dave", systemImage: "person.circle.fill"), Person(name: "Susan", systemImage: "person.circle.fill"), Person(name: "Robert", systemImage: "person.circle.fill"), Person(name: "Daniel", systemImage: "person.circle.fill") ] var body: some View { VStack(alignment: .leading, spacing: 0) { ScrollView(.horizontal) { HStack(spacing: 20) { ForEach(0..<people.count, id: \.self) { num in VStack { let person = people[num] Image(systemName: person.systemImage) .resizable() .aspectRatio(contentMode: .fit) .frame(height: 32) Text(person.name) .fixedSize() } .foregroundColor(selectedTab == num ? Color.blue : Color.gray) .onTapGesture { self.selectedTab = num } } } }.padding(.horizontal, 10) Spacer() .frame(height: 2) Rectangle().fill(.gray) .frame(height: 1) TabView(selection: $selectedTab) { ForEach(0..<people.count, id: \.self) { num in let person = people[num] Text(person.name) .tag(person.id) } } } } } struct Person: Identifiable { let id = UUID() let name: String let systemImage: String } Muchos thankos.
2
0
723
Aug ’23
Sorting CoreData Records by Creation Date
I have followed a tutorial written by Hacking with Swift ( https://www.hackingwithswift.com/books/ios-swiftui/how-to-combine-core-data-and-swiftui) about Core Data in SwiftUI. The Entity name is Student. And it has two properties: name (String), id (UUID). And the following is my code. import SwiftUI struct CoreView: View { @Environment(\.managedObjectContext) var managedObject @FetchRequest(sortDescriptors: []) var students: FetchedResults<Student> var body: some View { VStack { List(students) { student in Text(student.name ?? "Unknown") } Button { let firstNames = ["Gary", "Harry", "Elane", "Ray", "Nancy", "Jim", "Susan"] let lastNames = ["Johns", "McNamara", "Potter", "Thompson", "Hampton"] if let selectedFirstName = firstNames.randomElement(), let selectedLastName = lastNames.randomElement() { let newStudent = Student(context: managedObject) newStudent.id = UUID() newStudent.name = "\(selectedFirstName) \(selectedLastName)" try? managedObject.save() } } label: { Text("Add") } } } } struct CoreView_Previews: PreviewProvider { static var previews: some View { CoreView() .environmentObject(DataController()) } } If I list all records and then add a new student to the list, the app will insert the last addition at a random row. I wonder if I can order these records by the creation date? Muchos thankos
2
0
1.4k
Jul ’23
URLSession with URLRequest Timeout
I have the following lines of code to list some music titles from iTunes music. The code is 100% reproducible. import SwiftUI struct MusicView: View { @StateObject var viewModel = ViewModel() var body: some View { MusicListView(viewModel: viewModel) } } struct MusicListView: View { @ObservedObject var viewModel: ViewModel var body: some View { NavigationView { List(viewModel.results, id: \.self) { result in VStack(alignment: .leading) { Text("Track ID: \(result.trackId)") Text("Track name: \(result.trackName)") } } .task { do { try await viewModel.fetchMusic() } catch SessionError.badURL { print("Bad URL") } catch SessionError.invalidHTTPResponse { print("Invalid HTTP response") } catch SessionError.error(let err) { print("Error: \(err)") } catch { print("\(error.localizedDescription)") } } .navigationTitle("Music") } } } class ViewModel: ObservableObject { @Published var results: [Result] = [] func fetchMusic() async throws { guard let url = URL(string: "https://itunes.apple.com/search?term=classical+music&entity=song") else { throw SessionError.badURL } let urlRequest = URLRequest(url: url, timeoutInterval: 0.00) // <<<<<<<<<<<<< URLSession.shared.dataTask(with: urlRequest) { data, response, error in do { guard let data = data, error == nil else { throw SessionError.noData } guard let httpResponse = response as? HTTPURLResponse else { throw SessionError.invalidHTTPResponse } switch httpResponse.statusCode { case 200: let res = try JSONDecoder().decode(Response.self, from: data) DispatchQueue.main.async { self.results = res.results } case 400...499: throw SessionError.badURL default: fatalError() break } } catch { print(error.localizedDescription) } } .resume() } } struct Response: Codable { let resultCount: Int let results: [Result] } struct Result: Codable, Hashable { var trackId: Int var trackName: String var collectionName: String } enum SessionError: Error { case badURL case noData case decoding case invalidHTTPResponse case badRequest(statusCode: Int) case redirection(statusCode: Int) case server(statusCode: Int) case error(String) } As you see in the screenshot, I get some music titles listed. My question is why I get a list when in fact I have the URLRequest's timeout value set to 0.00? I haven't run it with an actual device. As far as I use an iPhone simulator, regardless of the timeout value that I set, I get data downloaded. I wonder why? Muchos thankos for reading
0
0
2.0k
Jul ’23
Version 15.0 beta 4 Horribly Slow when Debugged with Simulator
Oh, boy... Xcode has become more and more difficult to deal with. Today, I've dowloaded Version 15.0 beta 4. It took my 2019 iMac with 64 GB of RAM some 20 minutes just to launch an iPhone 14 Simulator and to let me see the home screen. Xcode takes 3 or 4 minutes to run code after I change just one line. I only have some 30 lines of code in total. It's a truly disappointing update. I wish they stop adding unnecessary features like code-folding animation to slow things down. import UIKit class ViewController: UIViewController { private let photoView: UIImageView = { let imageView = UIImageView() imageView.image = UIImage(systemName: "airplane") //imageView.clipsToBounds = true imageView.contentMode = .scaleAspectFit imageView.translatesAutoresizingMaskIntoConstraints = false return imageView }() override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .systemYellow view.addSubview(photoView) NSLayoutConstraint.activate([ photoView.centerXAnchor.constraint(equalTo: view.centerXAnchor), photoView.centerYAnchor.constraint(equalTo: view.centerYAnchor), photoView.widthAnchor.constraint(equalToConstant: 200), photoView.heightAnchor.constraint(equalToConstant: 200) ]) DispatchQueue.main.asyncAfter(deadline: .now() + 3) { self.runAirplaneAnimation() } } func runAirplaneAnimation() { photoView.addSymbolEffect(.pulse, animated: true) } }
0
0
568
Jul ’23
List Single Selection
I have some lines of code below where I can make multiple selections. import SwiftUI struct ContentView: View { @State private var selectedUsers: Set<String> = [] @State var users = ["Susan", "Kate", "Natalie", "Kimberly", "Taylor", "Sarah", "Nancy", "Katherine", "Nicole", "Linda", "Jane", "Mary", "Olivia", "Barbara"] var body: some View { VStack { List(selection: $selectedUsers) { ForEach(users, id: \.self) { user in Text(user) } } .environment(\.editMode, .constant(.active)) } } } So the blue selection symbol appears as shown in the screenshot above. That's good. But that's not what I'm after. I just want to select one row at a time. import SwiftUI struct ContentView: View { @State var selectedUser: String? @State var users = ["Susan", "Kate", "Natalie", "Kimberly", "Taylor", "Sarah", "Nancy", "Katherine", "Nicole", "Linda", "Jane", "Mary", "Olivia", "Barbara"] var body: some View { VStack { List(selection: $selectedUser) { ForEach(users, id: \.self) { user in Text(user) } } .environment(\.editMode, .constant(.active)) } } } In the lines of code above, I only let myself select one row at a time. And I don't get the blue selection symbol. I wonder why? I find two or three websites where they have similar lines of code and where they select one row at a time. And they have the blue selection symbol. Why don't I get it? Mucho thankos for reading.
4
0
1.4k
Jul ’23
Drawing a Pie without Path?
Drawing a pie isn't difficult if I do it with Path. import SwiftUI struct ContentView8: View { var body: some View { PieSlice(start: .degrees(-90), end: .degrees(120)) .fill(.pink) } } struct PieSlice: Shape { let start: Angle let end: Angle func path(in rect: CGRect) -> Path { var path = Path() let center = CGPoint(x: rect.midX, y: rect.midY) path.move(to: center) path.addArc(center: center, radius: rect.midX, startAngle: start, endAngle: end, clockwise: false) return path } } Actually, I want to animate this pie such that it will gradually deploy starting at -90 degrees. In the code above, I suppose I cannot animate the pie because the PieSlice guy isn't a View. Or can I? If I can't, is there an alternative way of drawing a pie so that I can animate it? Thanks a million. Señor Tomato Source Hostage Negotiator at Tomato Source Association of North America
2
0
644
Jul ’23
Saving Color from UIColorPickerViewController with UserDefaults
I've been trying to save a selected color with UserDefaults from UIColorPickerViewController. But I run into a color space fiasco. Anyway, here come my lines of code. class ViewController: UIViewController, UIColorPickerViewControllerDelegate { @IBOutlet weak var imageView: UIImageView! @IBAction func selectTapped(_ sender: UIButton) { let picker = UIColorPickerViewController() picker.delegate = self picker.selectedColor = .yellow picker.supportsAlpha = false present(picker, animated: true, completion: nil) } override func viewDidLoad() { super.viewDidLoad() } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) if let color = UserDefaultsUIColor.shared.readColor(key: "MyColor") { print("Color being read: \(color)") } } func colorPickerViewControllerDidFinish(_ viewController: UIColorPickerViewController) { let color = viewController.selectedColor print("Selected color: \(color)") UserDefaultsUIColor.shared.saveColor(color: viewController.selectedColor, key: "MyColor") } func colorPickerViewControllerDidSelectColor(_ viewController: UIColorPickerViewController) { imageView.backgroundColor = viewController.selectedColor } } class UserDefaultsUIColor { static let shared = UserDefaultsUIColor() func saveColor(color: UIColor, key: String) { let userDefaults = UserDefaults.standard do { let data = try NSKeyedArchiver.archivedData(withRootObject: color, requiringSecureCoding: false) as NSData? userDefaults.set(data, forKey: key) } catch { print("Error UserDefaults: \(error.localizedDescription)") } } func readColor(key: String) -> UIColor? { let userDefaults = UserDefaults.standard if let data = userDefaults.data(forKey: key) { do { if let color = try NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: data) { return color } } catch { print("Error UserDefaults") } } return nil } } I first start out with a yellow color (UIColor.yellow). And I select a color whose RGB values are 76, 212, 158, respectively. And the color picker guy returns the following. kCGColorSpaceModelRGB 0.298039 0.831373 0.619608 1 And I get the following in reading the saved color data object. UIExtendedSRGBColorSpace -0.270778 0.84506 0.603229 1 How can I save and read color data objects consistently? I could specify a color space when I save a color. But it doesn't go well. Muchos thankos Señor Tomato de Source
0
0
989
Jun ’23
Ending TextList Madness
I am just playing with NSTextList by creating a sample iOS app. The following is my code. import UIKit class ViewController: UIViewController { lazy var textView: UITextView = { let textView = UITextView() textView.text = "" textView.contentInsetAdjustmentBehavior = .automatic textView.backgroundColor = .white textView.font = UIFont.systemFont(ofSize: 20.0) textView.textColor = .black textView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ textView.widthAnchor.constraint(equalToConstant: 600.0), textView.heightAnchor.constraint(equalToConstant: 600.0) ]) return textView }() lazy var button: UIButton = { let button = UIButton() button.setTitle("End list", for: .normal) button.setTitleColor(.white, for: .normal) button.setTitleColor(.lightGray, for: .highlighted) button.backgroundColor = .black button.layer.cornerRadius = 8.0 button.addTarget(self, action: #selector(fixTapped), for: .touchUpInside) button.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ button.widthAnchor.constraint(equalToConstant: 100.0), button.heightAnchor.constraint(equalToConstant: 42.0) ]) return button }() override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .systemBlue view.addSubview(textView) view.addSubview(button) let tapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard)) view.addGestureRecognizer(tapGesture) NSLayoutConstraint.activate([ textView.centerXAnchor.constraint(equalTo: view.centerXAnchor), textView.centerYAnchor.constraint(equalTo: view.centerYAnchor) ]) NSLayoutConstraint.activate([ button.centerXAnchor.constraint(equalTo: view.centerXAnchor), button.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -20.0) ]) let list = NSTextList(markerFormat: .diamond, options: 0) list.startingItemNumber = 1 let paragraphStyle = NSParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle paragraphStyle.textLists = [list] let attributes = [NSAttributedString.Key.paragraphStyle: paragraphStyle, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 24.0)] let attributedStr = NSMutableAttributedString(string: "\n\n\n\n\n", attributes: attributes) textView.textStorage.setAttributedString(attributedStr) } @objc func fixTapped() { } @objc func dismissKeyboard() { view.endEditing(true) } } When the app launches itself, I get 5 lines of diamond guys as shown in the following screenshot. If I keep pressing the delete key with a connected keyboard, the list will be gone as shown below. But if I press the RETURN key several times, the diamond list will come back as shown below. So how can I end this June TextList madness? In code, I have the dismissKeyboard function if I can end this madness programmatically. Thanks, Señor Tomato Spaghetti Chief Janitor at Southeastern Tomato Spaghetti Trade Association
0
0
737
Jun ’23
Removing Apps from App Switcher?
I have a simple app that implements a custom URL scheme. When I enter the scheme for my app in Safari, the app will launch itself. So far, so good... Now, when I initiate the app switcher, I have my app and the web browser (Safari). Is there a way of not showing them in the app switcher? Or can I at least stop the web browser from appearing in the app switcher? Is it even possible for me to terminate Safari programmatically from my app, provided that that is not going to violate the app store guidelines? Thanks.
1
0
914
Apr ’23
Not Showing App Name in the Status Bar
I'm using the custom URL scheme to open my app through Safari. When the app appears, the status bar shows 'Safari' at the top-left corner. Is there a way of stopping the app from showing Safari's name? I don't want the user to tap the name and go back to Safari. There is nothing special in my SceneDelegate. So I wonder if it's probably the matter of settings in the Settings app? Thanks.
1
0
507
Apr ’23
Undo/Redo with DragGesture
I have a sample macOS app that I'm working on. I can run the exactly same lines of code below for iOS. For now, I'm running code for macOS since I can just press Command + z to undo the last action. Anyway, I have two Text View objects. Since TextView has the DragGesture gesture, I am able to freely move either of them. And I want to undo and redo their positions. So the following is what I have. import SwiftUI struct ContentView: View { @State var textViews: [TextView] = [TextView(text: "George"), TextView(text: "Susan")] var body: some View { VStack { ForEach(textViews, id: \.id) { textView in textView } } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } struct TextView: View { @Environment(\.undoManager) var undoManager @StateObject private var undoModel = UndoViewModel() @State private var dragOffset: CGSize = .zero @State private var position: CGSize = .zero let id = UUID() let text: String init(text: String) { self.text = text } var body: some View { ZStack { Text(text) .fixedSize() .padding(.vertical, 10) .offset(x: dragOffset.width + position.width, y: dragOffset.height + position.height) .gesture( DragGesture() .onChanged { self.dragOffset = $0.translation } .onEnded( { (value) in self.position.width += value.translation.width self.position.height += value.translation.height self.dragOffset = .zero undoModel.registerUndo(CGSize(width: position.width, height: position.height), in: undoManager) }) ) } } } class UndoViewModel: ObservableObject { @Published var point = CGSize.zero func registerUndo(_ newValue: CGSize, in undoManager: UndoManager?) { let oldValue = point undoManager?.registerUndo(withTarget: self) { [weak undoManager] target in target.point = oldValue // registers an undo operation to revert to old text target.registerUndo(oldValue, in: undoManager) // this makes redo possible } undoManager?.setActionName("Move") point = newValue // update the actual value } } Well, if I press Command + z after moving one of them, it won't return to the last position. What am I doing wrong? Muchos thankos.
Replies
1
Boosts
0
Views
717
Activity
Sep ’23
Making a Call to a Distant Struct
Let me say that I have three structs that are sequentially connected. ContentView -> FirstView -> SecondView And I want to make a call from SecondView to ContentView with a button tap. So I have the following lines of code. import SwiftUI struct ContentView: View { @State var goToFirst = false var body: some View { NavigationStack { VStack { NavigationLink { FirstView(callBack: { sayHello() }, goToSecond: $goToFirst) } label: { Text("Go to First") } } } .navigationDestination(isPresented: $goToFirst) { } } func sayHello() { print("Hello!") } } struct FirstView: View { @State var callBack: (() -> Void)? @Binding var goToSecond: Bool var body: some View { VStack { Button("Go to Second") { goToSecond.toggle() } } .navigationDestination(isPresented: $goToSecond) { SecondView(callBack: callBack) } } } struct SecondView: View { @State var callBack: (() -> Void)? var body: some View { VStack { Button("Tap me to make a call to ContentView") { callBack?() } } } } If I tap the button in SecondView, my ContentView will receive a call and call the sayHello function. Since ContentView and SecondView are not directly connected with each other, they have to through FirstView in this case. I wonder if there's a better or easier approach in having SecondView make a call to ContentView? In UIKit and Cocoa, you can make a delegate call to a distant class even when two classes are not directly connected with other. Using the notification is another option. In SwiftUI, I suppose you don't use either of them. Muchos thankos.
Replies
1
Boosts
0
Views
708
Activity
Sep ’23
Picker with ForEach
I have a ForEach loop with Range that I use with Picker. I'm using Range because I want to set startYear and endYear when View appears. The following is my code. import SwiftUI struct ProviderCalendarView: View { @State private var startYear: Int = 2023 @State private var endYear: Int = 2034 @State private var selectedYear = 3 var body: some View { VStack { HStack { Picker(selection: $selectedYear) { ForEach((startYear...endYear), id: \.self) { year in Text("\(year)") } } label: { } } } } } And the compiler says the following. Picker: the selection "3" is invalid and does not have an associated tag, this will give undefined results. It's not a critical error. But how can I stop it? Thanks.
Replies
2
Boosts
0
Views
1.4k
Activity
Aug ’23
DisclosureGroup with Swipe Actions and Contextual Menu
I have created a simple case to make my point as follows. import SwiftUI struct ContentView: View { var body: some View { ZStack { Color.yellow.ignoresSafeArea() VStack(alignment: .leading) { ForEach(Fruit.allCases, id: \.self) { fruit in DisclosureGroup(fruit.rawValue) { VStack { Text("1") Text("2") Text("3") } } .contextMenu { Button("Hello", action: { }) } } }.padding(.horizontal, 20) } } } enum Fruit: String, CaseIterable { case apple = "Apple" case grape = "Grape" case lemon = "Lemon" case orange = "Orange" case peach = "Peach" case pineapple = "Pineapple" case watermelon = "Watermelon" } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } What I want to do is show the contextual menu when the user long-presses a fruit name, which works. Yet, if I long-press a child inside the disclosure view, I also get the contextual menu, which is unintentional. Is there a simple way by which I can stop the contextual menu to appear if long-press a child inside the disclosure view? Muchos thankos
Replies
1
Boosts
0
Views
1.1k
Activity
Aug ’23
Exporting a Document with FileDocument, Not Packaged
I'm trying to export a document file. It contains a codable struct named NoteGroup. struct NoteGroup: Codable { let id: UUID let name: String let createAt: Date let children: [NoteChild] init(id: UUID = .init(), name: String = "", createAt: Date = .init(), children: [NoteChild]) { self.id = id self.name = name self.createAt = createAt self.children = children } } , which contains another object named NoteChild. I have a FileDocument struct as follows. import SwiftUI import UniformTypeIdentifiers struct Document: FileDocument { var document: NoteGroup static var readableContentTypes = [UTType.frogType] init(document: NoteGroup = NoteGroup(children: [NoteChild(id: UUID(), name: "", createAt: Date())])) { self.document = document } init(configuration: ReadConfiguration) throws { self.init() } func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper { do { let data = try getDocumentData() let jsonFileWrapper = FileWrapper(regularFileWithContents: data) let filename = "Note.frog" jsonFileWrapper.filename = filename let fileWrapper = FileWrapper(directoryWithFileWrappers: [filename: jsonFileWrapper]) return fileWrapper } catch { throw error } } private func getDocumentData() throws -> Data { let encoder = JSONEncoder() do { let data = try encoder.encode(document) return data } catch { throw error } } } extension UTType { public static let frogType = UTType(exportedAs: "com.example.frog") } And I export a file like the following. import SwiftUI import UniformTypeIdentifiers struct ContentView: View { @State private var showingExporter = false @State var doc = Document() var body: some View { VStack { Button("Tap to export") { showingExporter.toggle() } .fileExporter( isPresented: $showingExporter, document: doc, contentType: .frogType ) { result in switch result { case .success(let file): print(file) case .failure(let error): print(error) } } }.onAppear { doc = Document(document: NoteGroup(id: UUID(), name: "Kyle", createAt: Date(), children: [NoteChild(id: UUID(), name: "Nancy", createAt: Date())])) } } } Well, I have read this topic. And I've watched this video about Uniform Type Identifiers. Thanks to the video, I am able to export a file. Yet, I end up with a folder (Frog.frog), not a packaged file. There is a JSON file in it, though. What am I doing wrong? It's for iOS. La vida no es facil. Muchos thankos.
Replies
2
Boosts
0
Views
996
Activity
Aug ’23
Removing More?
I use the ForEach enumeration to list a View horizontally. And I get the following picture. So far, so good... If I select the 5th object or 6th one, something odd (< More) appears. I don't know where it comes from. I have never seen it before. How does it happen? I wonder how I can remove it? I have searched the net for a clue to no avail. I don't even know how to describe it. The following is my code. import SwiftUI struct ContentView: View { @State var selectedTab = 0 @State var addTapped = false @State var refresh = false @State var people = [ Person(name: "Alice", systemImage: "person.circle.fill"), Person(name: "Jane", systemImage: "person.circle.fill"), Person(name: "Dave", systemImage: "person.circle.fill"), Person(name: "Susan", systemImage: "person.circle.fill"), Person(name: "Robert", systemImage: "person.circle.fill"), Person(name: "Daniel", systemImage: "person.circle.fill") ] var body: some View { VStack(alignment: .leading, spacing: 0) { ScrollView(.horizontal) { HStack(spacing: 20) { ForEach(0..<people.count, id: \.self) { num in VStack { let person = people[num] Image(systemName: person.systemImage) .resizable() .aspectRatio(contentMode: .fit) .frame(height: 32) Text(person.name) .fixedSize() } .foregroundColor(selectedTab == num ? Color.blue : Color.gray) .onTapGesture { self.selectedTab = num } } } }.padding(.horizontal, 10) Spacer() .frame(height: 2) Rectangle().fill(.gray) .frame(height: 1) TabView(selection: $selectedTab) { ForEach(0..<people.count, id: \.self) { num in let person = people[num] Text(person.name) .tag(person.id) } } } } } struct Person: Identifiable { let id = UUID() let name: String let systemImage: String } Muchos thankos.
Replies
2
Boosts
0
Views
723
Activity
Aug ’23
Sorting CoreData Records by Creation Date
I have followed a tutorial written by Hacking with Swift ( https://www.hackingwithswift.com/books/ios-swiftui/how-to-combine-core-data-and-swiftui) about Core Data in SwiftUI. The Entity name is Student. And it has two properties: name (String), id (UUID). And the following is my code. import SwiftUI struct CoreView: View { @Environment(\.managedObjectContext) var managedObject @FetchRequest(sortDescriptors: []) var students: FetchedResults<Student> var body: some View { VStack { List(students) { student in Text(student.name ?? "Unknown") } Button { let firstNames = ["Gary", "Harry", "Elane", "Ray", "Nancy", "Jim", "Susan"] let lastNames = ["Johns", "McNamara", "Potter", "Thompson", "Hampton"] if let selectedFirstName = firstNames.randomElement(), let selectedLastName = lastNames.randomElement() { let newStudent = Student(context: managedObject) newStudent.id = UUID() newStudent.name = "\(selectedFirstName) \(selectedLastName)" try? managedObject.save() } } label: { Text("Add") } } } } struct CoreView_Previews: PreviewProvider { static var previews: some View { CoreView() .environmentObject(DataController()) } } If I list all records and then add a new student to the list, the app will insert the last addition at a random row. I wonder if I can order these records by the creation date? Muchos thankos
Replies
2
Boosts
0
Views
1.4k
Activity
Jul ’23
URLSession with URLRequest Timeout
I have the following lines of code to list some music titles from iTunes music. The code is 100% reproducible. import SwiftUI struct MusicView: View { @StateObject var viewModel = ViewModel() var body: some View { MusicListView(viewModel: viewModel) } } struct MusicListView: View { @ObservedObject var viewModel: ViewModel var body: some View { NavigationView { List(viewModel.results, id: \.self) { result in VStack(alignment: .leading) { Text("Track ID: \(result.trackId)") Text("Track name: \(result.trackName)") } } .task { do { try await viewModel.fetchMusic() } catch SessionError.badURL { print("Bad URL") } catch SessionError.invalidHTTPResponse { print("Invalid HTTP response") } catch SessionError.error(let err) { print("Error: \(err)") } catch { print("\(error.localizedDescription)") } } .navigationTitle("Music") } } } class ViewModel: ObservableObject { @Published var results: [Result] = [] func fetchMusic() async throws { guard let url = URL(string: "https://itunes.apple.com/search?term=classical+music&entity=song") else { throw SessionError.badURL } let urlRequest = URLRequest(url: url, timeoutInterval: 0.00) // <<<<<<<<<<<<< URLSession.shared.dataTask(with: urlRequest) { data, response, error in do { guard let data = data, error == nil else { throw SessionError.noData } guard let httpResponse = response as? HTTPURLResponse else { throw SessionError.invalidHTTPResponse } switch httpResponse.statusCode { case 200: let res = try JSONDecoder().decode(Response.self, from: data) DispatchQueue.main.async { self.results = res.results } case 400...499: throw SessionError.badURL default: fatalError() break } } catch { print(error.localizedDescription) } } .resume() } } struct Response: Codable { let resultCount: Int let results: [Result] } struct Result: Codable, Hashable { var trackId: Int var trackName: String var collectionName: String } enum SessionError: Error { case badURL case noData case decoding case invalidHTTPResponse case badRequest(statusCode: Int) case redirection(statusCode: Int) case server(statusCode: Int) case error(String) } As you see in the screenshot, I get some music titles listed. My question is why I get a list when in fact I have the URLRequest's timeout value set to 0.00? I haven't run it with an actual device. As far as I use an iPhone simulator, regardless of the timeout value that I set, I get data downloaded. I wonder why? Muchos thankos for reading
Replies
0
Boosts
0
Views
2.0k
Activity
Jul ’23
Version 15.0 beta 4 Horribly Slow when Debugged with Simulator
Oh, boy... Xcode has become more and more difficult to deal with. Today, I've dowloaded Version 15.0 beta 4. It took my 2019 iMac with 64 GB of RAM some 20 minutes just to launch an iPhone 14 Simulator and to let me see the home screen. Xcode takes 3 or 4 minutes to run code after I change just one line. I only have some 30 lines of code in total. It's a truly disappointing update. I wish they stop adding unnecessary features like code-folding animation to slow things down. import UIKit class ViewController: UIViewController { private let photoView: UIImageView = { let imageView = UIImageView() imageView.image = UIImage(systemName: "airplane") //imageView.clipsToBounds = true imageView.contentMode = .scaleAspectFit imageView.translatesAutoresizingMaskIntoConstraints = false return imageView }() override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .systemYellow view.addSubview(photoView) NSLayoutConstraint.activate([ photoView.centerXAnchor.constraint(equalTo: view.centerXAnchor), photoView.centerYAnchor.constraint(equalTo: view.centerYAnchor), photoView.widthAnchor.constraint(equalToConstant: 200), photoView.heightAnchor.constraint(equalToConstant: 200) ]) DispatchQueue.main.asyncAfter(deadline: .now() + 3) { self.runAirplaneAnimation() } } func runAirplaneAnimation() { photoView.addSymbolEffect(.pulse, animated: true) } }
Replies
0
Boosts
0
Views
568
Activity
Jul ’23
List Single Selection
I have some lines of code below where I can make multiple selections. import SwiftUI struct ContentView: View { @State private var selectedUsers: Set<String> = [] @State var users = ["Susan", "Kate", "Natalie", "Kimberly", "Taylor", "Sarah", "Nancy", "Katherine", "Nicole", "Linda", "Jane", "Mary", "Olivia", "Barbara"] var body: some View { VStack { List(selection: $selectedUsers) { ForEach(users, id: \.self) { user in Text(user) } } .environment(\.editMode, .constant(.active)) } } } So the blue selection symbol appears as shown in the screenshot above. That's good. But that's not what I'm after. I just want to select one row at a time. import SwiftUI struct ContentView: View { @State var selectedUser: String? @State var users = ["Susan", "Kate", "Natalie", "Kimberly", "Taylor", "Sarah", "Nancy", "Katherine", "Nicole", "Linda", "Jane", "Mary", "Olivia", "Barbara"] var body: some View { VStack { List(selection: $selectedUser) { ForEach(users, id: \.self) { user in Text(user) } } .environment(\.editMode, .constant(.active)) } } } In the lines of code above, I only let myself select one row at a time. And I don't get the blue selection symbol. I wonder why? I find two or three websites where they have similar lines of code and where they select one row at a time. And they have the blue selection symbol. Why don't I get it? Mucho thankos for reading.
Replies
4
Boosts
0
Views
1.4k
Activity
Jul ’23
Drawing a Pie without Path?
Drawing a pie isn't difficult if I do it with Path. import SwiftUI struct ContentView8: View { var body: some View { PieSlice(start: .degrees(-90), end: .degrees(120)) .fill(.pink) } } struct PieSlice: Shape { let start: Angle let end: Angle func path(in rect: CGRect) -> Path { var path = Path() let center = CGPoint(x: rect.midX, y: rect.midY) path.move(to: center) path.addArc(center: center, radius: rect.midX, startAngle: start, endAngle: end, clockwise: false) return path } } Actually, I want to animate this pie such that it will gradually deploy starting at -90 degrees. In the code above, I suppose I cannot animate the pie because the PieSlice guy isn't a View. Or can I? If I can't, is there an alternative way of drawing a pie so that I can animate it? Thanks a million. Señor Tomato Source Hostage Negotiator at Tomato Source Association of North America
Replies
2
Boosts
0
Views
644
Activity
Jul ’23
Saving Color from UIColorPickerViewController with UserDefaults
I've been trying to save a selected color with UserDefaults from UIColorPickerViewController. But I run into a color space fiasco. Anyway, here come my lines of code. class ViewController: UIViewController, UIColorPickerViewControllerDelegate { @IBOutlet weak var imageView: UIImageView! @IBAction func selectTapped(_ sender: UIButton) { let picker = UIColorPickerViewController() picker.delegate = self picker.selectedColor = .yellow picker.supportsAlpha = false present(picker, animated: true, completion: nil) } override func viewDidLoad() { super.viewDidLoad() } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) if let color = UserDefaultsUIColor.shared.readColor(key: "MyColor") { print("Color being read: \(color)") } } func colorPickerViewControllerDidFinish(_ viewController: UIColorPickerViewController) { let color = viewController.selectedColor print("Selected color: \(color)") UserDefaultsUIColor.shared.saveColor(color: viewController.selectedColor, key: "MyColor") } func colorPickerViewControllerDidSelectColor(_ viewController: UIColorPickerViewController) { imageView.backgroundColor = viewController.selectedColor } } class UserDefaultsUIColor { static let shared = UserDefaultsUIColor() func saveColor(color: UIColor, key: String) { let userDefaults = UserDefaults.standard do { let data = try NSKeyedArchiver.archivedData(withRootObject: color, requiringSecureCoding: false) as NSData? userDefaults.set(data, forKey: key) } catch { print("Error UserDefaults: \(error.localizedDescription)") } } func readColor(key: String) -> UIColor? { let userDefaults = UserDefaults.standard if let data = userDefaults.data(forKey: key) { do { if let color = try NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: data) { return color } } catch { print("Error UserDefaults") } } return nil } } I first start out with a yellow color (UIColor.yellow). And I select a color whose RGB values are 76, 212, 158, respectively. And the color picker guy returns the following. kCGColorSpaceModelRGB 0.298039 0.831373 0.619608 1 And I get the following in reading the saved color data object. UIExtendedSRGBColorSpace -0.270778 0.84506 0.603229 1 How can I save and read color data objects consistently? I could specify a color space when I save a color. But it doesn't go well. Muchos thankos Señor Tomato de Source
Replies
0
Boosts
0
Views
989
Activity
Jun ’23
Ending TextList Madness
I am just playing with NSTextList by creating a sample iOS app. The following is my code. import UIKit class ViewController: UIViewController { lazy var textView: UITextView = { let textView = UITextView() textView.text = "" textView.contentInsetAdjustmentBehavior = .automatic textView.backgroundColor = .white textView.font = UIFont.systemFont(ofSize: 20.0) textView.textColor = .black textView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ textView.widthAnchor.constraint(equalToConstant: 600.0), textView.heightAnchor.constraint(equalToConstant: 600.0) ]) return textView }() lazy var button: UIButton = { let button = UIButton() button.setTitle("End list", for: .normal) button.setTitleColor(.white, for: .normal) button.setTitleColor(.lightGray, for: .highlighted) button.backgroundColor = .black button.layer.cornerRadius = 8.0 button.addTarget(self, action: #selector(fixTapped), for: .touchUpInside) button.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ button.widthAnchor.constraint(equalToConstant: 100.0), button.heightAnchor.constraint(equalToConstant: 42.0) ]) return button }() override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .systemBlue view.addSubview(textView) view.addSubview(button) let tapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard)) view.addGestureRecognizer(tapGesture) NSLayoutConstraint.activate([ textView.centerXAnchor.constraint(equalTo: view.centerXAnchor), textView.centerYAnchor.constraint(equalTo: view.centerYAnchor) ]) NSLayoutConstraint.activate([ button.centerXAnchor.constraint(equalTo: view.centerXAnchor), button.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -20.0) ]) let list = NSTextList(markerFormat: .diamond, options: 0) list.startingItemNumber = 1 let paragraphStyle = NSParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle paragraphStyle.textLists = [list] let attributes = [NSAttributedString.Key.paragraphStyle: paragraphStyle, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 24.0)] let attributedStr = NSMutableAttributedString(string: "\n\n\n\n\n", attributes: attributes) textView.textStorage.setAttributedString(attributedStr) } @objc func fixTapped() { } @objc func dismissKeyboard() { view.endEditing(true) } } When the app launches itself, I get 5 lines of diamond guys as shown in the following screenshot. If I keep pressing the delete key with a connected keyboard, the list will be gone as shown below. But if I press the RETURN key several times, the diamond list will come back as shown below. So how can I end this June TextList madness? In code, I have the dismissKeyboard function if I can end this madness programmatically. Thanks, Señor Tomato Spaghetti Chief Janitor at Southeastern Tomato Spaghetti Trade Association
Replies
0
Boosts
0
Views
737
Activity
Jun ’23
Removing Apps from App Switcher?
I have a simple app that implements a custom URL scheme. When I enter the scheme for my app in Safari, the app will launch itself. So far, so good... Now, when I initiate the app switcher, I have my app and the web browser (Safari). Is there a way of not showing them in the app switcher? Or can I at least stop the web browser from appearing in the app switcher? Is it even possible for me to terminate Safari programmatically from my app, provided that that is not going to violate the app store guidelines? Thanks.
Replies
1
Boosts
0
Views
914
Activity
Apr ’23
Not Showing App Name in the Status Bar
I'm using the custom URL scheme to open my app through Safari. When the app appears, the status bar shows 'Safari' at the top-left corner. Is there a way of stopping the app from showing Safari's name? I don't want the user to tap the name and go back to Safari. There is nothing special in my SceneDelegate. So I wonder if it's probably the matter of settings in the Settings app? Thanks.
Replies
1
Boosts
0
Views
507
Activity
Apr ’23