Post

Replies

Boosts

Views

Activity

CoreBluetooth Central, Observing Multiple Peripherals
A device running with the following lines of code can receive a message from a peripheral. In this manner, though, I can only receive messages from one peripheral since the service and characteristic IDs are hardcoded in CentralViewModel.swift. So my question is how I can observe messages from multiple peripherals. Thanks. import SwiftUI struct ContentView: View { var body: some View { NavigationStack { VStack { NavigationLink(destination: CentralView()) { Text("Central") } .buttonStyle(.borderedProminent) .padding() } } } } // CentralView.swift // import SwiftUI struct CentralView: View { @StateObject var central: CentralViewModel = CentralViewModel() var body: some View { Text(central.message) .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) .padding(20) .onDisappear { central.stopAction() } } } // CentralViewModel.swift // import Foundation import CoreBluetooth class CentralViewModel: NSObject, ObservableObject { @Published var message: String = "" var serviceUUID: CBUUID! var characteristicUUID: CBUUID! var centralManager: CBCentralManager! var discoveredPeripheral: CBPeripheral? var transferCharacteristic: CBCharacteristic? var writeIterationsComplete = 0 //var connectionIterationsComplete = 0 let defaultIterations = 5 var data: Data = Data() override init() { super.init() self.serviceUUID = CBUUID(string: "994F8A12-FE8E-4CCB-BD7B-1AE989A32853") self.characteristicUUID = CBUUID(string: "F4BD0CA2-7581-40E2-A517-1CE275A3A749") centralManager = CBCentralManager(delegate: self, queue: nil, options: [CBCentralManagerOptionShowPowerAlertKey: true]) } func stopAction() { centralManager.stopScan() } private func cleanup() { guard let discoveredPeripheral = discoveredPeripheral, case .connected = discoveredPeripheral.state else { return } for service in (discoveredPeripheral.services ?? [] as [CBService]) { for characteristic in (service.characteristics ?? [] as [CBCharacteristic]) { if characteristic.uuid == characteristicUUID && characteristic.isNotifying { self.discoveredPeripheral?.setNotifyValue(false, for: characteristic) } } } centralManager.cancelPeripheralConnection(discoveredPeripheral) } private func writeData() { guard let discoveredPeripheral = discoveredPeripheral, let transferCharacteristic = transferCharacteristic else { return } while writeIterationsComplete < defaultIterations && discoveredPeripheral.canSendWriteWithoutResponse { writeIterationsComplete += 1 } if writeIterationsComplete == defaultIterations { discoveredPeripheral.setNotifyValue(false, for: transferCharacteristic) } } } extension CentralViewModel: CBCentralManagerDelegate { func centralManagerDidUpdateState(_ central: CBCentralManager) { switch central.state { case .poweredOn: print("Power on") startScanningForPeripherals() return case .poweredOff : print("Power off") return case .resetting: print("Resetting") return case .unauthorized: print("Unauthorized") return case .unknown: print("Unknown") return case .unsupported: print("Unsupported") return @unknown default: print("An unknown central manager state has occurred") return } } func startScanningForPeripherals() { self.centralManager.scanForPeripherals(withServices: [self.serviceUUID], options: nil) } func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) { guard RSSI.intValue >= -50 else { return } if discoveredPeripheral != peripheral { print("Peripheral discovered") discoveredPeripheral = peripheral centralManager.connect(peripheral, options: nil) } } func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { peripheral.delegate = self peripheral.discoverServices([serviceUUID]) print("Service discovered") } } extension CentralViewModel: CBPeripheralDelegate { func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) { if error != nil { cleanup() return } guard let peripheralServices = peripheral.services else { return } for service in peripheralServices { peripheral.discoverCharacteristics([characteristicUUID], for: service) } } func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { if let error = error { print("Error discovering characteristics: \(error.localizedDescription)") cleanup() return } guard let serviceCharacteristics = service.characteristics else { return } for characteristic in serviceCharacteristics where characteristic.uuid == characteristicUUID { transferCharacteristic = characteristic peripheral.setNotifyValue(true, for: characteristic) } } func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) { if let error = error { print("Error changing notification state: \(error.localizedDescription)") return } guard characteristic.uuid == characteristicUUID else { return } if characteristic.isNotifying { print("Notification began on \(characteristic)") } else { print("Notification stopped on \(characteristic). Disconnecting") cleanup() } } func peripheralIsReady(toSendWriteWithoutResponse peripheral: CBPeripheral) { print("Peripheral is ready to send data to YOU!") } func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) { if let error = error { print("Error discovering characteristics: \(error.localizedDescription)") cleanup() return } guard let characteristicData = characteristic.value, let stringFromData = String(data: characteristicData, encoding: .utf8) else { return } print("Received \(characteristicData.count) bytes: \(stringFromData)") if stringFromData == "EOM" { message = String(data: data, encoding: .utf8) ?? "" writeData() } else { data.append(characteristicData) } } }
1
0
811
Mar ’24
Text .onTapGesture Never Called When Shown with .onLongPressGesture
I'm showing a Text View when a button with an image is long-pressed. import SwiftUI struct ContentView: View { @Environment(\.colorScheme) var colorScheme var isDark: Bool { return colorScheme == .dark } @State private var showLabel = false var body: some View { Button(action: { }) { VStack { ZStack { Image(systemName: "swift") .resizable() .aspectRatio(contentMode: .fit) .frame(width: 32) .padding(.horizontal, 40) .padding(.vertical, 6) .background(.gray.opacity(0.2), in: RoundedRectangle(cornerRadius: 10)) .onTapGesture { showLabel.toggle() } .onLongPressGesture(minimumDuration: 2) { print("Long pressed...") showLabel.toggle() } if showLabel { Text("Help Content") .font(.caption) .foregroundStyle(!isDark ? .white : .black) .padding(10) .background(!isDark ? .black : .white, in: Rectangle()) .onTapGesture { print("hey") showLabel.toggle() } .offset(x: 120) } } } } } } So a Text View will appear as shown in the image above. But its .onTapGesture is never called. I wonder why? Thanks.
1
0
897
Jul ’24
Send a Closure from NSViewRepresentable (or UIViewRepresentable) to ContentView?
I'm using NSTableView with NSViewRepresentable in my SwiftUI ContentView. I'm letting the user right-click on a table row such that the application will recognize the row number, which is achieved. The following is what I have so far. struct ContentView: View { @State private var rowSelection = -1 VStack { TableView(tableData: someData, selectedRow: $rowSelection) } } struct TableView: NSViewRepresentable { @Binding var tableData: [Dictionary<String, String>] @Binding var selectedRow: Int func makeNSView(context: Context) -> NSScrollView { let scrollView = NSScrollView(frame: .zero) let tableView = NSTableView() tableView.delegate = context.coordinator tableView.dataSource = context.coordinator let contextMenu = NSMenu() let copyRowMenuItem = NSMenuItem(title: "Copy Row", action: #selector(Coordinator.tableRowAction(_:)), keyEquivalent: "") contextMenu.addItem(copyRowMenuItem) copyRowMenuItem.target = context.coordinator tableView.menu = contextMenu scrollView.documentView = tableView scrollView.hasVerticalScroller = true scrollView.hasHorizontalScroller = true scrollView.autohidesScrollers = true return scrollView } func updateNSView(_ nsView: NSScrollView, context: Context) { } func makeCoordinator() -> Coordinator { return Coordinator(tableData: $tableData, tableInfo: $tableInfo, selectedRow: $selectedRow, rowSelected: $rowSelected) } class Coordinator: NSObject, NSTableViewDelegate, NSTableViewDataSource { @Binding var tableData: [Dictionary<String, String>] @Binding var selectedRow: Int init(tableData: Binding<[Dictionary<String, String>]>, tableInfo: Binding<[PragmaModel]>, selectedRow: Binding<Int>, rowSelected: Binding<Bool>) { self._tableData = tableData self._rowSelected = rowSelected } func numberOfRows(in tableView: NSTableView) -> Int { return tableData.count } func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? { for i in 0..<tableInfo.count { let col = NSString(format: "%i", i) as String let identifier = NSString(format: "Column%i", i) as String if ((tableColumn?.identifier)!.rawValue == identifier) { let data = tableData[row][col] return data } } return nil } func tableViewSelectionDidChange(_ notification: Notification) { let tv = notification.object as! NSTableView if tv.selectedRow >= 0 { selectedRow = tv.selectedRow } } @objc func tableRowAction(_ sender: Any) { // closure // } } } The contextual menu works. Yet, the application needs to know when a row is clicked on. So I want to send a closure back to ContentView. How can I do that, por favor? Muchos thankos.
1
0
505
Aug ’24
Button.background(Color) under the Light Appearance
I'm trying to set the background color of a button with label like the following for a macOS application. I haven't run it for iOS. VStack(spacing: 20) { HStack(spacing: 32) { Button { showGuide.toggle() } label: { Text("Hello") .font(.title3) .frame(width: 190, height: 36) } .foregroundStyle(.primary) .background(.yellow) .clipShape(.capsule) .shadow(color: .red, radius: 8) Button { } label: { Text("Good morning") .font(.title3) .frame(width: 190, height: 36) } .foregroundStyle(.primary) .background(.pink) .clipShape(.capsule) .shadow(color: .red, radius: 8) } } .frame(maxWidth: .infinity, alignment: .center) Interestingly, those two buttons have a white background color under the light appearance as shown below. And it will get the designated background color under the dark appearance as shown below. So why don't I get the buttons colored under the light appearance? I can't figure out why it happens. Does anybody know why? Thanks.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
1
0
552
Aug ’24
All Item Row Positions After List onMove(perform:)
Using List to list an array of an object isn't a problem. For example, I have simple lines of code below to list an array of some guy. struct ContentView: View { @State private var selectedFieldItem: FieldItem? private var fieldListView: some View { List(selection: $selectedFieldItem) { ForEach(fieldItems.indices, id: \.self) { index in Button { ... } label: { let fieldItem = fieldItems[index] HStack(spacing: 10) { Text("\(fieldItem.name)") } .frame(maxWidth: .infinity, alignment: .leading) } .buttonStyle(.borderless) } .onMove(perform: fieldlocate) } .listStyle(.plain) } private func fieldlocate(from source: IndexSet, to destination: Int) { fieldItems.move(fromOffsets: source, toOffset: destination) } } As you see in the picture below, I can move a row up and down. A problem that I now have with List is that I cannot know how those rows are rearranged after one of them is moved up or down. In Cocoa, you can tell the positions of all rows after one moves with tableView(_:acceptDrop:row:dropOperation:) I think I have done the same in UIKit. Can we tell the current row numbers of List items in SwiftUI? Like Item 0 moves from Row 0 to Row 2 item 1 moves from Row 1 to Row 0 item 2 moves from Row 2 to Row 1 after one drags Item 0 from the top to the bottom? Muchos thankos.
Topic: UI Frameworks SubTopic: SwiftUI
1
0
399
Aug ’24
Showing SwiftUI Below NSStatusItem When Button is Clicked on Over SwiftUI View
I see a lot of tutorials that show how to open a SwiftUI View when a NSStatusItem is clicked on. That's not what I want. I need to show a SwiftUI View when I click on a button over SwiftUI View. So far the following is what I have. import SwiftUI @main struct MyStatusApp_App: App { @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate var body: some Scene { WindowGroup { ContentView() } } } #if os(macOS) class AppDelegate: NSObject, NSApplicationDelegate { var statusItem: NSStatusItem! private var popover: NSPopover? func applicationDidFinishLaunching(_ notification: Notification) { hideTitleBar() NSApp.setActivationPolicy(.accessory) statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) if let button = statusItem.button { if let image = NSImage(named: "statusImage") { button.image = image } } } #endif // ContentView // import SwiftUI struct ContentView: View { @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate var body: some View { VStack { Button("Click me!") { let popOver = NSPopover() popOver.contentViewController = NSHostingController(rootView: NotificationView()) appDelegate.statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) if let statusBarbutton = appDelegate.statusItem.button { popOver.show(relativeTo: statusBarbutton.bounds, of: statusBarbutton, preferredEdge: .minY) } } } .frame(width: 200, height: 100) } } If I run the application and click on the button (orange arrow) over ContentView, a guy from NotificationView will appear (green rectangle). That's good. But it appears not below the status item (red arrow). It's positioned at an odd location. It's way below the status item guy. What am I doing wrong? Muchos thankos. I guess site's add image function is broken. It doesn't show my screenshot. ![]("https://developer.apple.com/forums/content/attachment/7e19bf2e-439d-4ed0-a03c-740b77e94e24" "title=Screenshot.jpg;width=364;height=400")
Topic: UI Frameworks SubTopic: SwiftUI Tags:
1
0
721
Aug ’24
Writing to Printer with Core Bluetooth
I have a very cheap Bluetooth-connected printer. And I want to print out a word or two via Core Bluetooth. It's an iOS app with the SwiftUI framework. The following is what I have for an ObservableObject class. import Foundation import CoreBluetooth class BluetoothManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeripheralDelegate { @Published var connectedDevices: [CBPeripheral] = [] @Published var powerOn = false @Published var peripheralConnected = false private var centralManager: CBCentralManager! private var peripheralName = "LX-D02" private var connectedPeripheral: CBPeripheral? private var writeCharacteristic: CBCharacteristic? private let serviceUUID = CBUUID(string:"5833FF01-9B8B-5191-6142-22A4536EF123") private let characteristicUUID = CBUUID(string: "FFE1") override init() { super.init() self.centralManager = CBCentralManager(delegate: self, queue: nil) } func startScanning() { if centralManager.state == .poweredOn { centralManager.scanForPeripherals(withServices: nil, options: nil) } } func centralManagerDidUpdateState(_ central: CBCentralManager) { if central.state == .poweredOn { powerOn = true print("Bluetooth is powered on") } else { print("Bluetooth is not available") } } func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) { if !connectedDevices.contains(peripheral) { if let localName = advertisementData["kCBAdvDataLocalName"] as? String { if localName == peripheralName { connectedDevices.append(peripheral) centralManager.connect(peripheral, options: nil) centralManager.stopScan() peripheralConnected = true print("Connected: \(peripheral.identifier.uuidString)") } } } } func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { connectedPeripheral = peripheral peripheral.delegate = self let services = [serviceUUID] peripheral.discoverServices(services) //discoverServices(peripheral: peripheral) } func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: (any Error)?) { guard let error = error else { print("Failed connection unobserved") return } print("Error: \(error.localizedDescription)") } func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) { if let error = error { print("Failing to discover servies: \(error.localizedDescription)") return } discoverCharacteristics(peripheral: peripheral) } /* Return all available services */ private func discoverServices(peripheral: CBPeripheral) { peripheral.discoverServices(nil) } private func discoverCharacteristics(peripheral: CBPeripheral) { guard let services = peripheral.services else { return } for service in services { peripheral.discoverCharacteristics(nil, for: service) } } func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { guard let characteristics = service.characteristics else { return } for characteristic in characteristics { let characteristicUUID = characteristic.uuid print("Discovered characteristic: \(characteristicUUID)") peripheral.setNotifyValue(true, for: characteristic) if characteristic.properties.contains(.writeWithoutResponse) { writeCharacteristic = characteristic print("You can write!!!") // Never read... } if characteristic.properties.contains(.write) { print("You can write?") writeCharacteristic = characteristic // Being read... } } func writeToPrinter() { guard let peripheral = connectedPeripheral else { print("Ughhh...") return } if let characteristic = writeCharacteristic { if let data = "Hello".data(using: .utf8, allowLossyConversion: true) { peripheral.writeValue(data, for: characteristic, type: .withoutResponse) peripheral.writeValue(data, for: characteristic, type: .withResponse) // -> Message sent successfully } } } func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) { if let error = error { print("Writing error: \(error.localizedDescription)") return } print("Message sent successfully") } } My app has no trouble connecting to the bluetooth-connected printer. Initially, I called discoverServices(peripheral:) to get all services And I get a service identifier (5833FF01-9B8B-5191-6142-22A4536EF123) for my printer. peripheral(_:didDiscoverCharacteristicsFor:error:) doesn't return a thing for .writeWithoutResponse but does return a characteristic for .write. Eventually, if I call writeToPrinter(), peripheral.writeValue(data, for: characteristic, type: .withoutResponse) returns WARNING: Characteristic <CBCharacteristic: 0x3019040c0, UUID = 5833FF02-9B8B-5191-6142-22A4536EF123, properties = 0x8, value = (null), notifying = NO> does not specify the "Write Without Response" property - ignoring response-less write If I call peripheral.writeValue(data, for: characteristic, type: .withResponse) , there is no error. But I get no output from the printer. What am I doing wrong? Thanks.
1
0
475
Jan ’25
Release Build Configuration as Release Fails Preview
I have a simple SwiftUI project with two basic build configurations (Debug, Release) as shown below. I now choose Build > Scheme > Edit Scheme under Product and select Release as the current build configuration as shown below. And the Preview canvas exhibit errors. If I click on the Diagnostics button, it says under PREVIEW UPDATE ERROR OptimizationLevelError: not building -Onone ”BuildSchemeCrazyDaughter.app” needs -Onone Swift optimization level to use previews (current setting is -O) What does that mean and why don't I get the preview for the Release build configuration? Thanks.
1
0
183
Mar ’25
Opening a New Document from File URL?
I have a sample document-based macOS app. I understand that you can open a new window or a new tab with some text. import SwiftUI struct ContentView: View { @Binding var document: TexDocument @Environment(\.newDocument) var newDocument var body: some View { VStack(spacing: 0) { topView } } private var topView: some View { Button { newDocument(TexDocument(text: "A whole new world!")) } label: { Text("Open new window") .frame(width: 200) } } } Suppose that I have a path to a text file whose security-scoped bookmark can be resolved with a click of a button. I wonder if you can open a new window or a new tab with the corresponding content?. I have done that in Cocoa. I hope I can do it in SwiftUI as well. Thanks.
1
0
95
May ’25
CoreData Data Sharing with AppGroup
I have the following lines of code to access data through CoreData. import Foundation import CoreData import CloudKit class CoreDataManager { static let instance = CoreDataManager() let container: NSPersistentCloudKitContainer let context: NSManagedObjectContext init() { container = NSPersistentCloudKitContainer(name: "ABC") container.loadPersistentStores(completionHandler: { (storeDescription, error) in if let error = error as NSError? { print(error.userInfo) } }) context = container.viewContext context.automaticallyMergesChangesFromParent = true context.mergePolicy = NSMergePolicy(merge: .mergeByPropertyObjectTrumpMergePolicyType) } func save() { do { try container.viewContext.save() print("Saved successfully") } catch { print("Error in saving data: \(error.localizedDescription)") } } } I have confirmed that I can share data between iPhone and iPad. Now, I need to use AppGroup as well. I have changed my code as follows. import Foundation import CoreData import CloudKit class CoreDataManager { static let shared = CoreDataManager() let container: NSPersistentContainer let context: NSManagedObjectContext init() { container = NSPersistentCloudKitContainer(name: "ABC") container.persistentStoreDescriptions = [NSPersistentStoreDescription(url: FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "some group name")!.appendingPathComponent("CoreDataMama.sqlite"))] container.loadPersistentStores(completionHandler: { (description, error) in if let error = error as NSError? { print("Unresolved error \(error), \(error.userInfo)") } }) context = container.viewContext context.automaticallyMergesChangesFromParent = true context.mergePolicy = NSMergePolicy(merge: .mergeByPropertyObjectTrumpMergePolicyType) } func save() { do { try container.viewContext.save() print("Saved successfully") } catch { print("Error in saving data: \(error.localizedDescription)") } } } Other files being unaltered, my sample apps aren't sharing data. What am I doing wrong? Just FYI, I'm using actual devices. Thank you for your reading this topic.
1
0
108
May ’25
iBeacon with CLMonitor Slow
The other day I was playing with iBeacon and found out that CLBeaconIdentityConstraint will be deprecated after iOS 18.5. So I've written code with BeaconIdentityCondition in reference to this Apple's sample project. import Foundation import CoreLocation let monitorName = "BeaconMonitor" @MainActor public class BeaconViewModel: ObservableObject { private let manager: CLLocationManager static let shared = BeaconViewModel() public var monitor: CLMonitor? @Published var UIRows: [String: [CLMonitor.Event]] = [:] init() { self.manager = CLLocationManager() self.manager.requestWhenInUseAuthorization() } func startMonitoringConditions() { Task { print("Set up monitor") monitor = await CLMonitor(monitorName) await monitor!.add(getBeaconIdentityCondition(), identifier: "TestBeacon") for identifier in await monitor!.identifiers { guard let lastEvent = await monitor!.record(for: identifier)?.lastEvent else { continue } UIRows[identifier] = [lastEvent] } for try await event in await monitor!.events { guard let lastEvent = await monitor!.record(for: event.identifier)?.lastEvent else { continue } if event.state == lastEvent.state { continue } UIRows[event.identifier] = [event] UIRows[event.identifier]?.append(lastEvent) } } } func updateRecords() async { UIRows = [:] for identifier in await monitor?.identifiers ?? [] { guard let lastEvent = await monitor!.record(for: identifier)?.lastEvent else { continue } UIRows[identifier] = [lastEvent] } } func getBeaconIdentityCondition() -> CLMonitor.BeaconIdentityCondition { CLMonitor.BeaconIdentityCondition(uuid: UUID(uuidString: "abc")!, major: 123, minor: 789) } } It works except that my sample app can take as long as 90 seconds to see event changes. You would get an instant update with an fashion (CLBeacon and CLBeaconIdentityConstraint). Is there anything that I can do to see changes faster? Thanks.
1
0
78
Jun ’25
Animating Shape
I'm trying to animate a shape. The following is my code. import SwiftUI struct ContentView7: View { @State private var goingLeft = true @State private var goingUp = true var body: some View { ZStack { Color.green.ignoresSafeArea() CustomShape(quadDistance: goingUp ? 0: 60.0, horizontalDeviation: 0.0) .fill(Color.red) .ignoresSafeArea() .animation(.easeInOut(duration: 1.0).repeatForever(), value: goingUp) }.onAppear { goingUp = false } } } struct CustomShape: Shape { var quadDistance: CGFloat var horizontalDeviation: CGFloat func path(in rect: CGRect) -> Path { Path { path in path.move(to: CGPoint(x: -horizontalDeviation, y: 0)) path.addLine(to: CGPoint(x: rect.maxX + horizontalDeviation , y: 0)) path.addLine(to: CGPoint(x: rect.maxX + horizontalDeviation, y: rect.midY)) path.addLine(to: CGPoint(x: -horizontalDeviation, y: rect.midY)) path.move(to: CGPoint(x: -horizontalDeviation, y: rect.midY)) path.addQuadCurve(to: CGPoint(x: rect.maxX + horizontalDeviation, y: rect.midY), control: CGPoint(x: rect.midX, y: rect.midY + quadDistance)) } } } Well, my intension is to move the center of the convex point up and down. But the shape won't animate itself. What am I doing wrong? Muchos thankos.
0
0
313
Feb ’22
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
673
Jun ’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
942
Jun ’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
533
Jul ’23