Post

Replies

Boosts

Views

Activity

Reply to How to replace a specific character in a string inside a UITextField?
@Claude31 Here is what I get in console: Put print statements before check on how much math symbols string has: func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { //all possible math operation symbols user can add let symbolsSet = Set(["+","-","x","/"]) var amountOfSymbols = 0 let numberString = textField.text ?? "" guard let range = Range(range, in: numberString) else { return false } let updatedString = numberString.replacingCharacters(in: range, with: string) let correctDecimalString = updatedString.replacingOccurrences(of: formatter.groupingSeparator, with: "") let completeString = correctDecimalString.replacingOccurrences(of: formatter.decimalSeparator, with: ".") //current math symbol user add let symbol = symbolsSet.filter(completeString.contains).last ?? "" //if user add math symbol to an empty string - do not insert if string == symbol, numberString.count == 0 { return false }  print("symbol", symbol)  print("string", string)  print("updatedString", updatedString)  print("correctDecimalString", correctDecimalString) print("completeString", completeString) //count how much math symbols string has. If more that one - do not insert, string can have only one completeString.forEach { character in if symbolsSet.contains(String(character)) { amountOfSymbols += 1 } } if amountOfSymbols > 1 { return false } } Here is the console output after each character I type: Press 2 symbol  string 2 updatedString 2 correctDecimalString 2 completeString 2 Press + symbol + string + updatedString 2+ correctDecimalString 2+ completeString 2+ Press - symbol + string - updatedString 2+- correctDecimalString 2+- completeString 2+- 2.Put print statements after check on how much math symbols string has: if amountOfSymbols > 1 { return false }   print("symbol", symbol)   print("string", string)   print("updatedString", updatedString)   print("correctDecimalString", correctDecimalString)   print("completeString", completeString) Here is the console output after each character I type: Press 2 symbol  string 2 updatedString 2 correctDecimalString 2 completeString 2 Press + symbol + string + updatedString 2+ correctDecimalString 2+ completeString 2+ Press - Nothing is printed
Jul ’22
Reply to How to replace a specific character in a string inside a UITextField?
@Claude31 The closer I can get is to use the code: var completeString = correctDecimalString.replacingOccurrences(of: formatter.decimalSeparator, with: ".") if amountOfSymbols > 1 {   let newSymbol = completeString.last?.description ?? ""   completeString.removeAll { symbolsSet.contains(String($0))}     textField.text = completeString+newSymbol     return false } But then If I'll add math symbol again it will change the first number which I want to avoid: https://share.cleanshot.com/tmFiAr
Jul ’22
Reply to How to format numbers in a textField with math equation string?
Here is how I solved my problem: func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { let formatter = NumberFormatter() formatter.numberStyle = .decimal formatter.maximumFractionDigits = 2 formatter.locale = .current formatter.roundingMode = .down //set of possible math operations let symbolsSet = Set(["+","-","x","/"]) let numberString = textField.text ?? "" guard let range = Range(range, in: numberString) else { return false } let updatedString = numberString.replacingCharacters(in: range, with: string) let correctDecimalString = updatedString.replacingOccurrences(of: formatter.decimalSeparator, with: ".") let completeString = correctDecimalString.replacingOccurrences(of: formatter.groupingSeparator, with: "") //receive math symbol user typed let symbol = symbolsSet.filter(completeString.contains).first ?? "" //receive number of symbols in a String. If user wants to type more than one math symbol - do not insert let amountOfSymbols = completeString.filter({String($0) == symbol}).count if amountOfSymbols > 1 { return false } //receive numbers typed by user let numbersArray = completeString.components(separatedBy: symbol) //check for each number - if user wants to type more than one decimal sign - do not insert for number in numbersArray { let amountOfDecimalSigns = number.filter({$0 == "."}).count if amountOfDecimalSigns > 1 { return false } } guard let firstNumber = Double(String(numbersArray.first ?? "0")) else { return true } guard let secondNumber = Double(String(numbersArray.last ?? "0")) else { return true } let firstFormattedNumber = formatter.string(for: firstNumber) ?? "" let secondFormattedNumber = formatter.string(for: secondNumber) ?? "" // if user typed math symbol - show 2 numbers and math symbol, if not - show just first typed number textField.text = completeString.contains(symbol) ? "\(firstFormattedNumber)\(symbol)\(secondFormattedNumber)" : "\(firstFormattedNumber)" return string == formatter.decimalSeparator }
Jul ’22
Reply to UIGestureRecognizer works outside of UIView borders instead of inside
Here is how I was able to achieve my needs: Switch popupView.isUserInteractionEnabled = false to start swipe recognises not only outside the view change my didSwipe method onto this: @objc private func didSwipe(_ sender:UISwipeGestureRecognizer) { guard let window = UIApplication.shared.windows.first(where: {$0.isKeyWindow}) else { return } let tappedArea = sender.location(in: popupView) let popupFrame = CGRect(x: tappedArea.x, y: tappedArea.y, width: popupView.frame.width, height: popupView.frame.height) if sender.direction == .up, window.frame.contains(popupFrame) { UIView.animate(withDuration: 0.15, delay: 0.0, options: .curveLinear) { self.popupView.center.y -= 50 } completion: { _ in self.popupView.removeFromSuperview() } self.isRemovedBySwipe = true } } Now I am able to swipe out the popup only when swipe inside its borders and ignore swipes outside of it.
Mar ’22
Reply to How to enable UIScrollView scroll on a nested UITableView while dragging a cell?
What is the UILabel Here on GIF: https ://cln.sh/j45g83 ? It's a grey text just below the searchBar and before the first cell. Take a look I made a new gif for you: https://cln.sh/ajHYYt When do you want the label to move ? When you scroll the tableView ? Yes, when I drag tableView, label should follow If so, why not just put this label in HeaderView of the table ? Can I put it just like it shows now? (right aligned, the same color and size) Can you suggest how can I move it correctly? Having a tableView (which is a scrollView) inside another scrollView is complex to handle. Basically I can't scroll my tableView. It has a fixed size since I have the exact amount of cells and it's not possible to delete or add to it. Just reorder. The only reason I put it all inside a scrollView is to be able to interact with a Navigation Bar Large Title: if I put a label before TableView without scrollView, then when I drag tableView up - the NavBar won't animate (going up), it will be like fixed since label will interfere...
Feb ’22
Reply to How to distinguish textFields of a prototype cell in tableView?
Here is how I tried to achieve my needs: func textFieldDidChangeSelection(_ textField: UITextField) { let tapLocation = textField.convert(textField.bounds.origin, to: tableView) guard let pickedCurrencyIndexPath = tableView.indexPathForRow(at: tapLocation) else { return } //Append only if array don't have the IndexPath already if !indexPathsArray.contains(where: {$0 == pickedCurrencyIndexPath}) { indexPathsArray.append(pickedCurrencyIndexPath) } //I need to compare only 2 last IndexPaths, so delete the most old at position 0 in array if indexPathsArray.count > 2 { indexPathsArray.remove(at: 0) } for indexPath in indexPathsArray { if indexPath.last != indexPath.first { //user clicked on a new textfield,clear all so he can type a new number numberFromTextField = 0 textField.placeholder = "0" textField.text = "" } } print(indexPathsArray) } With this code I almost achieved what I need except when I click on a new textField I can't type anything... GIF: cln.sh/sGNlBE
Feb ’22
Reply to How to set a UITableView Editing Mode from a swipe action?
Also I have one more related problem I can't fix there. My prototype cell has a numberTextField (0 by default). When I entering the edit mode I want it to be hidden and back to visible when edit mode is off. Here is the code for that behaviour in cellForRowAt(): cell.numberTextField.isHidden = tableView.isEditing ? true : false return cell But the textField is still visible when I entering the edit mode: https://cln.sh/0hXOKg Any solutions comes in mind? I tried to initialise cell as: let cell = tableView.dequeueReusableCell(withIdentifier: "converterCell") as! ConverterTableViewCell and assess its textfield property within turnEditing() but no luck.
Feb ’22
Reply to NSFetchedResultsController error: 'no object at index 5 in section at index 0'
Claude, it seems I found out what the reason of that crash was. This is what my print() tryings gave: https://cln.sh/jpB5tG What is a pickedCurrency: this is a global variable of custom type Currency which I created to receive its attribute currentValue (Double, 87.88). I need that value only from the picked to edit cell. After I use that value for calculation in cellForRowAt() and result of the calculation fills all other cells which is not in edit now. I define pickedCurrency in textFieldDidBeginEditing() because there I receive the exact row of Currency I picked to edit: func textFieldDidBeginEditing(_ textField: UITextField) { pickedCurrency = fetchedResultsController.object(at: IndexPath(row: textField.tag, section: 0)) numberFromTextField = 0 textField.textColor = UIColor(named: "BlueColor") textField.placeholder = "0" textField.text = "" } And then use it's value in cellForRowAt to calculate all other cells values based on pickedCell value: 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.flag.image = currencyManager.showCurrencyFlag(currency.shortName ?? "notFound") cell.shortName.text = currency.shortName cell.fullName.text = currency.fullName cell.numberTextField.tag = indexPath.row cell.numberTextField.delegate = self if let number = numberFromTextField, let pickedCurrency = pickedCurrency { cell.numberTextField.text = currencyManager.performCalculation(with: number, pickedCurrency, currency) } return cell } It seems when I delete a lot of cells and then click on random cell to edit it's not updates its IndexPath(row: textField.tag, section: 0)... And this is why when I call reloadData() it refreshes pickedCurrency. Maybe there is a way to receive Currency object I picked for editing in cellForRowAt()?
Feb ’22
Reply to NSFetchedResultsController error: 'no object at index 5 in section at index 0'
Could you show code with reloadData() in completion handler, in case we can find a way to improve smoothness. Sure. I just add reloadData() to .delete case. Check the GIF with animation in that case: https://cln.sh/F551dX 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)                 tableView.reloadData()             }         case .insert:             if let newIndexPath = newIndexPath {                 tableView.insertRows(at: [newIndexPath], with: .none)             }         default:             tableView.reloadData()         }     } Then version with asyncAfter .now + 0.5. Check the GIF with animation: https://cln.sh/WK6F8r 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) DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {                 self.tableView.reloadData() }             }         case .insert:             if let newIndexPath = newIndexPath {                 tableView.insertRows(at: [newIndexPath], with: .none)             }         default:             tableView.reloadData()         }     } Can't you set a var fetchedCompleted that would be set true after fetch But where is the fetch ends? Where to call that var?
Feb ’22
Reply to NSFetchedResultsController error: 'no object at index 5 in section at index 0'
But difficult to say what to write exactly without the complete code of the class. Attached is the complete class code: Complete Code Usually, this is handled by doing the call (to self.tableView.reloadData()) in a completion handler of the calling and I can call just tableView.reloadData() in the completion hander, everything works perfect too, except the cell deletion animation lost its smoothness, it became fast and ugly. And if I use +0.2 seconds the animation saves the smoothness like it was. Also this error happens only when I delete a lot of cells one after another (> 10 cells) like on the GIF from initial post. If I will delete 1-2 cells - there won't be any error and no need to call tableView.reloadData().
Feb ’22
Reply to NSFetchedResultsController error: 'no object at index 5 in section at index 0'
Where do you do it ? Nowhere except at FRC delegate method didChange I showed in the initial post... Could you also show cellForRowAt delegate func ? Sure, there it is: 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.flag.image = currencyManager.showCurrencyFlag(currency.shortName ?? "notFound") cell.shortName.text = currency.shortName cell.fullName.text = currency.fullName cell.numberTextField.tag = indexPath.row cell.numberTextField.delegate = self if let number = numberFromTextField, let pickedCurrency = pickedCurrency { cell.numberTextField.text = currencyManager.performCalculation(with: number, pickedCurrency, currency) } return cell } Also show you the method where I reload all rows except one in editing mode: func textFieldDidChangeSelection(_ textField: UITextField) { let activeTextFieldIndexPath = IndexPath(row: textField.tag, section: 0) pickedCurrency = fetchedResultsController.object(at: activeTextFieldIndexPath) guard let currencyObjects = fetchedResultsController.fetchedObjects?.count else {return} var nonActiveIndexPaths = [IndexPath]() for object in 0..<currencyObjects where object != textField.tag { nonActiveIndexPaths.append(IndexPath(row: object, section: 0)) } tableView.reloadRows(at: nonActiveIndexPaths, with: .none) } Then, after, you may call reloadData() once datasource is updated I found out that if I use reloadData() + 0.2 seconds after deletion, everything works good. But FRC should call reloadData automatically, I shouldn't use it manually... case .delete: if let indexPath = indexPath { tableView.deleteRows(at: [indexPath], with: .none) DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { self.tableView.reloadData() } }
Feb ’22
Reply to How to format a number(text) in UITextField when user is typing?
Thank you very much, Claude! After playing with the code I found that this version is good for me: func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { let formatter = NumberFormatter() formatter.usesGroupingSeparator = true formatter.numberStyle = .decimal formatter.decimalSeparator = "." formatter.groupingSeparator = " " let completeString = textField.text!.replacingOccurrences(of: formatter.groupingSeparator, with: "") + string guard let value = Double(completeString) else { return false } numberFromTextField = value let formattedNumber = formatter.string(from: NSNumber(value: value)) ?? "" textField.text = formattedNumber if string.isEmpty { return true } return string == formatter.decimalSeparator } But I have one problem: while pressing backspace on keyboard the first press doesn't remove the last character in string, the second and the next presses do. I think the problem is in my if string.isEmpty { return true } line as I found out when you press the backspace it returns an empty string... Short gif with problem: https://cln.sh/OZOslI Any thoughts what's the problem might be?
Feb ’22