Posts

Post marked as solved
3 Replies
167 Views
I have a tableView with a prototype cell with a textField. I need to figure out how to do next: when a user press on one of the textFields in tableView - he activate it and types a number editing is done, keyboard hides if user press second time on the same textField again then the number he typed first time should be saved and be editable (like he typed number 2, then activate textField again and can edit like 23) if user pressed on a different textField, I need to run different code (reset to 0). I can determine indexPath of currently edit textField: func textFieldDidChangeSelection(_ textField: UITextField) {         let tapLocation = textField.convert(textField.bounds.origin, to: tableView)         guard let pickedCurrencyIndexPath = tableView.indexPathForRow(at: tapLocation) else { return } I thought about storing the row of picked TextField, then if user clicks on the same TF and it matches the saved one - run one code, if row is changed (i.e. user clicked on anther TF) - run different code. I tried to create an array which holds pressed TF IndexPath, but it doesn't store the previous textField indexPath, each time I press on textField it prints only the current pressed without what I pressed before. What can I do there?
Posted Last updated
.
Post marked as solved
5 Replies
411 Views
I need to turn on a tableView editing mode by clicking on one of its cells from swipe action "move": Code for swipe action: override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { let move = UIContextualAction(style: .normal, title: "Переместить") { (action, view, completionHandler) in self.turnEditing() completionHandler(true) } move.backgroundColor = UIColor(named: "CalmBlueColor") let configuration = UISwipeActionsConfiguration(actions: [move]) return configuration } turnEditing() function: func turnEditing() { if tableView.isEditing { tableView.isEditing = false } else { tableView.isEditing = true } } When I press on the swipe actions it's just closes, without going to editing mode... Here is the GIF: https://cln.sh/ilEN9F Is it possible to go into editing mode from a swipe action or only from a barButtonItem + IBAction?
Posted Last updated
.
Post marked as solved
1 Replies
155 Views
I have a method which need an Array of Strings for further calculations. For now I set up this array through UserDefaults, because it should store small (3-characters string) amount of data: func setRow(for currency: Currency, in currencies: [Currency]) { // initialise array from UserDefaults var currencyRowsArray = UserDefaults.standard.stringArray(forKey: "currencyRowsArray") ?? [String]() // calculations with the array (add or remove string object to\from it if currency.isForConverter { currencyRowsArray.append(currency.shortName!) } else { guard let row = currencyRowsArray.firstIndex(of: currency.shortName!) else { return } currencyRowsArray.remove(at: row) currency.rowForConverter = 0 } for (row, object) in currencyRowsArray.enumerated() { for currency in currencies { if object == currency.shortName { currency.rowForConverter = Int32(row) } } } // save array back to UserDefaults after calculations UserDefaults.standard.set(currencyRowsArray, forKey: "currencyRowsArray") } But since I have a CoreData implemented also I decided to store it in CoreData rather than UserDefaults. Here is how I tried to implement that: 1.I have entity Currency, so I created an attribute currencyRowsArray: Type - Transformable, Custom Class - [String], Transformer - NSSecureUnarchiveFromData, Optional - YES (because I don't need it to initialize every time a created a Currency object with other attributes) 2.In the setRow method: let request: NSFetchRequest<Currency> = Currency.fetchRequest() request.predicate = NSPredicate(format: "currencyRowsArray IN %@", currencyRowsArray) 3.Initialize the empty array to fill it later with fetched Data: currencyRowsArray = [String]() 4.Fetch the array: do { let fetchResult = try context.fetch(request) currencyRowsArray = fetchedResult as! [String] } catch { print(error) } 5.After calculations save the changes: do { try context.save() } catch { print(error) } Full changed code of the setRow method: func setRow(for currency: Currency, in currencies: [Currency]) { var currencyRowsArray = [String] () let request: NSFetchRequest<Currency> = Currency.fetchRequest() request.predicate = NSPredicate(format: "currencyRowsArray IN %@", currencyRowsArray) do { let fetchResult = try context.fetch(request) currencyRowsArray = fetchResult as! [String] } catch { print(error) } if currency.isForConverter { currencyRowsArray.append(currency.shortName!) } else { guard let row = currencyRowsArray.firstIndex(of: currency.shortName!) else { return } currencyRowsArray.remove(at: row) currency.rowForConverter = 0 } for (row, object) in currencyRowsArray.enumerated() { for currency in currencies { if object == currency.shortName { currency.rowForConverter = Int32(row) } } } do { try context.save() } catch { print(error) } } But every time the array loads as empty and when I make change, array doesn't add or remove items in it. What I did wrong?
Posted Last updated
.
Post marked as solved
1 Replies
135 Views
I have a VC1 with a UITableView which populate it cells with a NSFetchedResultsController. I want VC1 tableView to place all new added cells to the bottom of tableView, therefore I created a CoreData attribute: itemNumberForVC1(type Int32). The cells can be added only from VC2. That's why I need to pass the numberOfObjects from VC1 method numberOfRowsInSection to VC2. The numberOfObjects is in the following method of VC1: override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { guard let numberOfObjects = fetchedResultsController.sections?[section].numberOfObjects else {return 0} return numberOfObjects } I need to use the numberOfObjects in my VC2 didSelectRowAt method: override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let currency = fetchedResultsController.object(at: indexPath) currency.isForConverter = !currency.isForConverter //currency.itemNumberForVC1 = numberOfObjects - 1 <- calculation: there I should receive the actual //number of objects from VC1 and assign the number to picked currency, and it should appear in VC1 //at the bottom of its TableView coreDataManager.save() } I tried to use delegates but inside VC2 didSelectRowAt the result is always 0. The max result I've got is created a method in VC2, which takes numberOfObjects as a parameter, call it in VC1 and then I have that number inside the VC2 but only in borders of the method. I can't move it to VC2 didSelectRowAt (tried through a global variable). How can I do that efficiently?
Posted Last updated
.
Post marked as solved
11 Replies
281 Views
I have a UITableView which populate it cells with a NSFetchedResultsController based on CoreData attribute isForConverter which is Bool and should be true to be displayed. isForConverter state sets in another ViewController. When I want to delete some cells from the UITableView and after access cells which wasn't deleted I receive the error: *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'no object at index 5 in section at index 0' There is a GIF with the problem: https://cln.sh/M1aI9Z My code for deleting cells. I don't need to delete it from database, just change it isForConverter from true to false: override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { if editingStyle == .delete { let currency = fetchedResultsController.object(at: indexPath) currency.isForConverter = false coreDataManager.save() } } NSFetchedResultsController Setup and delegates: func setupFetchedResultsController() { let predicate = NSPredicate(format: "isForConverter == YES") fetchedResultsController = coreDataManager.createCurrencyFetchedResultsController(with: predicate) fetchedResultsController.delegate = self try? fetchedResultsController.performFetch() } func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) { tableView.beginUpdates() } func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) { tableView.endUpdates() } func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) { switch type { case .update: if let indexPath = indexPath { tableView.reloadRows(at: [indexPath], with: .none) } case .move: if let indexPath = indexPath, let newIndexPath = newIndexPath { tableView.moveRow(at: indexPath, to: newIndexPath) } case .delete: if let indexPath = indexPath { tableView.deleteRows(at: [indexPath], with: .none) } case .insert: if let newIndexPath = newIndexPath { tableView.insertRows(at: [newIndexPath], with: .none) } default: tableView.reloadData() } } } I noticed that if I just add tableView.reloadData() to tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) then everything works good. But deletion animation is really fast and antsy. Also according to docs I should not use tableView.reloadData() with NSFetchedResultsController... How to fix that behaviour?
Posted Last updated
.
Post marked as solved
3 Replies
317 Views
I have a converter which converts numbers. When user is typing a number from NumberPad keyboard into my cell textField I want it to be formatted and displayed in real time like: 1000 -> 1 000 10000 -> 10 000 1000000 -> 1 000 000 1000,77 -> 1 000,77 I know it should happen in textFieldDidChangeSelection method. Here is mine: func textFieldDidChangeSelection(_ textField: UITextField) { numberFromTextField = Double(textField.text!) //This is for reload visible Rows in my tableView. Might not needed in my question's context. let activeTextFieldIndexPath = IndexPath(row: textField.tag, section: 0) var visibleIndexPaths = [IndexPath]() for indexPath in tableView.indexPathsForVisibleRows! { if indexPath != activeTextFieldIndexPath { visibleIndexPaths.append(indexPath) } } tableView.reloadRows(at: visibleIndexPaths, with: .none) } There I have a global variable numberFromTextField which I made because I need to use the Double version of textField.text in my separate calculation methods. How can I implement above formatted behaviour and at the same time save numberFromTextField = Double(textField.text!) as I need it in my different calculations?
Posted Last updated
.
Post marked as solved
8 Replies
341 Views
I have a UITableViewController and a Prototype cell with a UITextField. When I change a textField.text in one of the cells I want it to be changed in all other cells which my tableView now have (for example multiply number by 2). This is the behaviour I want to implement: https://cln.sh/xjw5Od (short video) Should I trigger a certain textField delegate method or it should be made in another way? My code for now: override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { cell.numberTextField.delegate = self } //Delegate methods func textFieldDidBeginEditing(_ textField: UITextField) { textField.text = "" textField.textColor = UIColor.systemBlue } func textFieldDidEndEditing(_ textField: UITextField, reason: UITextField.DidEndEditingReason) { textField.text = "0" textField.textColor = UIColor.black }
Posted Last updated
.
Post not yet marked as solved
1 Replies
165 Views
I have 1 UIViewController with a type of CustomTableView and 1 UITableViewController with a type of usual UITableView. Both conform to NSFetchedResultsControllerDelegate and implemented its delegate methods with the repeated code. For now it's in extensions. Is it possible to move that code out to a separate swift file? I tried to move it to separate file with class NSFetchedResultsView, but when I copy that delegate methods to the new file, it doesn't know anything about tableView inside it's methods... How can I separate that methods properly? Delegate methods I want to separate: func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) { tableView.beginUpdates() } func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) { tableView.endUpdates() } func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) { switch type { case .update: if let indexPath = indexPath { tableView.reloadRows(at: [indexPath], with: .none) } case .move: if let indexPath = indexPath, let newIndexPath = newIndexPath { tableView.moveRow(at: indexPath, to: newIndexPath) } case .delete: if let indexPath = indexPath { tableView.deleteRows(at: [indexPath], with: .none) } case .insert: if let newIndexPath = newIndexPath { tableView.insertRows(at: [newIndexPath], with: .none) } default: tableView.reloadData() } }
Posted Last updated
.
Post marked as solved
11 Replies
1.4k Views
I have 2 ViewControllers: VC1 populates its tableView based on CoreData attribute isPicked, which is bool and show only items with true state. VC2 is a second Modal (not Full Screen) View Controller which allow user to change the state of isPicked attribute: check and uncheck item (make it true or false). The idea is user picks needed items end dismiss the VC2 and the items should appear in VC1. I have implemented NSFetchedResultsController to both VC1 and VC2. And as soon as I press on first item (i.e. change isPicked state to true) I receive the error from VC1: [error] fault: Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (2) must be equal to the number of rows contained in that section before the update (1), plus or minus the number of rows inserted or deleted from that section (0 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out). Here is how I change a state of item in VC2 (true\false): guard let currencyArray = fetchedResultsController.fetchedObjects else { return } let cell = tableView.cellForRow(at: indexPath) as! PickCurrencyTableViewCell for currency in currencyArray { if currency.shortName == cell.shortName.text { currency.isPicked = !currency.isPicked coreDataManager.save() } } } Here is my VC1 NSFetchedResultsController implementation: super.viewDidLoad() setupFetchedResultsController() } func setupFetchedResultsController() { let predicate = NSPredicate(format: "isForConverter == YES") fetchedResultsController = createCurrencyFetchedResultsController(and: predicate) fetchedResultsController.delegate = self try? fetchedResultsController.performFetch() } func createCurrencyFetchedResultsController(with request: NSFetchRequest<Currency> = Currency.fetchRequest(), and predicate: NSPredicate? = nil, sortDescriptor: [NSSortDescriptor] = [NSSortDescriptor(key: "shortName", ascending: true)]) -> NSFetchedResultsController<Currency> { request.predicate = predicate request.sortDescriptors = sortDescriptor return NSFetchedResultsController(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil) } Here is my VC1 NSFetchedResultsController delegate: func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) { tableView.beginUpdates() } func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) { tableView.endUpdates() } func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) { if let indexPath = indexPath, let newIndexPath = newIndexPath { switch type { case .update: tableView.reloadRows(at: [indexPath], with: .none) case .move: tableView.moveRow(at: indexPath, to: newIndexPath) case .delete: tableView.deleteRows(at: [indexPath], with: .none) case .insert: tableView.insertRows(at: [indexPath], with: .none) default: tableView.reloadData() } } } } And finally my VC1 Table View methods: override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { let currency = fetchedResultsController.sections![section] return currency.numberOfObjects } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "converterCell", for: indexPath) as! ConverterTableViewCell let currency = fetchedResultsController.object(at: indexPath) cell.shortName.text = currency.shortName cell.fullName.text = currency.fullName return cell } When I reload the app, picked item shows itself in VC1 (which caused crash). But every change in VC2 crashes the app again with the same error. I don't have any methods to delete items in VC1 or so. I need that VC1 tableView just show or hide items according to isPicked state made from VC2. What I missed in my code?
Posted Last updated
.
Post marked as solved
2 Replies
446 Views
I have a networking method which returns 33 results. I save it in a CoreData database. Networking method starts every time an App launches (in ViewDidLoad). After every launch my CoreData database turns to be filled in with the same 33 results. So every launch I have 33, 66, 99, etc entries in it with the same names and attributes. In my Entity Currency I have a unique attribute id like shortName (EUR, USD, etc). How can I check what I have in CoreData after every Networking and if that shortName already exists just update its another attribute currentValue? And if there is no such shortName then fully create and save there. The goal is to have only 33 entity's in my CoreData with recent information from Networking. Thank you for a help in advance!
Posted Last updated
.
Post marked as solved
4 Replies
807 Views
Hi Everyone. Can anybody give me a tip where to look for solving this wrong app icon display in Launchpad. I created an .icns file with all needed icon sizes according to Apple guide but after manually implementing this file into the app I received that this new icon displays bigger than others in Launchpad. How to fix that?
Posted Last updated
.