Post

Replies

Boosts

Views

Activity

How to correctly pass NSFetchedResultsController's numberOfObjects from one VC to another?
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?
1
0
481
Feb ’22
How to setup and fetched Strings Array in CoreData?
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?
1
0
821
Feb ’22
How to set a UITableView Editing Mode from a swipe action?
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?
5
0
2.4k
Feb ’22
How to distinguish textFields of a prototype cell in tableView?
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?
3
0
625
Feb ’22
How to limit the amount of entered decimal characters in textField?
I have a textField where user can enter a number which can has a decimals. I want to restrict the amount of decimal characters possible to enter till 4. I'm trying to reach this behaviour with this code: let split = completeString.components(separatedBy: ".") if split.count == 2 { print(split.last!) if split.last!.count >= 4 { return false } } Full code for shouldChangeCharactersIn method: func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { let formatter = converterManager.setupNumberFormatter() let textString = textField.text ?? "" guard let range = Range(range, in: textString) else { return false } let updatedString = textString.replacingCharacters(in: range, with: string) let correctDecimalString = updatedString.replacingOccurrences(of: ",", with: ".") let completeString = correctDecimalString.replacingOccurrences(of: formatter.groupingSeparator, with: "") let split = completeString.components(separatedBy: ".") if split.count == 2 { print(split.last!) if split.last!.count >= 4 { return false } } numberFromTextField = completeString.isEmpty ? 0 : Double(completeString) guard completeString.count <= 12 else { return false } guard !completeString.isEmpty else { return true } textField.text = formatter.string(for: numberFromTextField) return string == formatter.decimalSeparator } } This code successfully split the string, give me the numbers user enters after decimal sign, but: textField shows 3 character instead of 4 print shows that despite textField shows 3, when I press 4th digits in shows in the split array when after 4th digit I continue to press numbers it changes last digit. I want if there is a 4 numbers entered already allow only delete not change the last number GIF with behaviour: https://cln.sh/5vf1iq How to fix that behaviour?
1
0
1.3k
Feb ’22
How to enable UIScrollView scroll on a nested UITableView while dragging a cell?
I have a UIScrollView with a UILabel + UITableView as a subview. Here is how it looks in a Storyboard: https://cln.sh/k2QiFS In my tableView I have implemented a drag and drop for its cells through edit mode. When I drag a cell and move it down to the bottom of the tableView, the scrollView doesn't scroll and not showing cells which is currently out of the visible area. So I should drop the cell, scroll the UIScrollView manually and again drag the cell. Here is the behaviour on GIF: https://cln.sh/j45g83 If I get rid out of scrollView, the basic tableView behaviour automatically scrolls to the up/bottom, but the reason I added the scrollView is to be able the UILabel be scrolled too. Here is my code for moveRowAt(): func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) { var currencies = fetchedResultsController.fetchedObjects! let currency = fetchedResultsController.object(at: sourceIndexPath) currencies.remove(at: sourceIndexPath.row) currencies.insert(currency, at: destinationIndexPath.row) for (index, currency) in currencies.enumerated() { currency.rowForCurrency = Int32(index) } coreDataManager.save() } How can I turn on a scroll for my UIScrollView?
3
0
3.4k
Feb ’22
UIGestureRecognizer works outside of UIView borders instead of inside
I've implemented a popup notification in my app to show user the data was being updated successfully or not. This popup is just a UIView instantiated from .xib. Here is screenshot of elements hierarchy in xib file: https://cln.sh/te8JhM Also I wanted the ability to swipe out the popup if user don't want to see a full time it shows itself on the screen. To do that I have implemented a UIGestureRecognizer: private func configureTapGesture() { guard let window = UIApplication.shared.windows.first(where: {$0.isKeyWindow}) else { return } let swipe = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipes(_:))) swipe.direction = .up window.addGestureRecognizer(swipe) } @objc private func handleSwipes(_ sender:UISwipeGestureRecognizer) { if sender.direction == .up { UIView.animate(withDuration: 0.15, delay: 0.0, options: .curveLinear) { self.popupView.center.y -= 50 } completion: { _ in self.popupView.removeFromSuperview() } self.isRemovedByTap = true } } When I run the app it works only if I swipe out anywhere but not inside the popupView. Please check for the GIF: https://cln.sh/sNm5RN If I replace a target from self (Popup class) to popupView (UIVisualEffectView, which is a rounded rectangle you see on GIF), then I receive an error unrecognized selector sent to instance Here is a my full custom Popup class where I initialize the view, configure, animate and show it: import UIKit class PopupView: UIView { @IBOutlet weak var popupView: UIVisualEffectView! @IBOutlet weak var symbol: UIImageView! @IBOutlet weak var titleLabel: UILabel! @IBOutlet weak var descriptionLabel: UILabel! private var isRemovedByTap = false override init(frame: CGRect) { super.init(frame: frame) configure() } required init?(coder: NSCoder) { super.init(coder: coder) configure() } private func configure() { if let views = Bundle.main.loadNibNamed("PopupView", owner: self) { guard let view = views.first as? UIView else { return } view.frame = bounds addSubview(view) } } private func configurePopup() { guard let window = UIApplication.shared.windows.first(where: {$0.isKeyWindow}) else { return } popupView.layer.cornerRadius = 20 popupView.clipsToBounds = true popupView.center.x = window.frame.midX popupView.translatesAutoresizingMaskIntoConstraints = true window.addSubview(popupView) } private func animatePopup() { UIView.animate(withDuration: 0.15, delay: 0.0, options: .curveLinear) { self.popupView.center.y += 34 } completion: { _ in UIView.animate(withDuration: 0.15, delay: 10.0, options: .curveLinear) { self.popupView.center.y -= 50 } completion: { _ in if !self.isRemovedByTap { self.popupView.removeFromSuperview() } } } } private func configureTapGesture() { guard let window = UIApplication.shared.windows.first(where: {$0.isKeyWindow}) else { return } let swipe = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipes(_:))) swipe.direction = .up window.addGestureRecognizer(swipe) } @objc private func handleSwipes(_ sender:UISwipeGestureRecognizer) { if sender.direction == .up { UIView.animate(withDuration: 0.15, delay: 0.0, options: .curveLinear) { self.popupView.center.y -= 50 } completion: { _ in self.popupView.removeFromSuperview() } self.isRemovedByTap = true } } func showPopup(title: String, message: String, symbol: UIImage) { titleLabel.text = title descriptionLabel.text = message self.symbol.image = symbol configurePopup() animatePopup() configureTapGesture() } } What should I change to be able to swipe out only when inside the popupView rounded rectangle?
1
0
847
Mar ’22
How to format numbers in a textField with math equation string?
I'm trying to format numbers in a UITextField consists of math equation String: "number + number". At the moment I can type just a single number, then convert it to Double -> format with NSNumberFormatter -> convert back to String -> assign to textField.text: https://i.stack.imgur.com/4qQza.gif The code: 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 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: "") guard let value = Double(completeString) else { return false } let formattedNumber = formatter.string(for: value) textField.text = formattedNumber return string == formatter.decimalSeparator } Now I want to add a calculation functionality and display a simple math equation in a textField as "number + number", but each number should be formatted as shown on above screenshot. Example (but without formatting): https://i.stack.imgur.com/W7Jet.gif I can't properly implement that. The logic for me was: track the String each time new char inserts -> if it has math sign extract numbers -> convert them to Double -> format with NSNumberFormatter -> convert back to String -> construct a new String "number + number". The code I tried: if let firstString = completeString.split(separator: "+").first, let secondString = completeString.split(separator: "+").last { guard let firstValue = Double(firstString) else { return false } guard let secondValue = Double(secondString) else { return false } let firstFormattedNumber = formatter.string(for: firstValue) let secondFormattedNumber = formatter.string(for: secondValue) textField.text = "\(firstFormattedNumber ?? "") + \(secondFormattedNumber ?? "")" // another try if completeString.contains("+") { let stringArray = completeString.components(separatedBy: "+") for character in stringArray { print(character) guard let value = Double(character) else { return false } guard let formattedNumber = formatter.string(for: value) else { return false } textField.text = "\(formattedNumber) + " } } But it's not working properly. I tried to search but didn't find any similar questions. Test project on GitHub How can I format the numbers from such a string?
2
0
801
Jul ’22
Visual Effect View's Blur doesn't work on UIView from xib
I created a custom Numpad keyboard through xib and wanted to add a blur effect to its background. So I add a Visual Effect View in xib: https://i.stack.imgur.com/QjiwP.png Main View and Visual Effect View background color is set to Default and I also tried to use Clear Color. The problem is when I initialize the Numpad, background has a light grey color without any blur effect: https://i.stack.imgur.com/LebUA.png How to add a blur effect to the Numpad so yellow square can be blurred and visible? Code for NumpadView: import UIKit class NumpadView: UIView { @IBOutlet weak var resetButton: NumpadButton! @IBOutlet weak var decimalButton: NumpadButton! var target: UITextInput? var view: UIView? init(target: UITextInput, view: UIView) { super.init(frame: .zero) self.target = target self.view = view initializeSubview() } required init?(coder: NSCoder) { super.init(coder: coder) initializeSubview() } func initializeSubview() { let xibFileName = "NumpadView" let view = Bundle.main.loadNibNamed(xibFileName, owner: self, options: nil)![0] as! UIView self.addSubview(view) view.frame = self.bounds self.autoresizingMask = [.flexibleWidth, .flexibleHeight] } } Code for initializing in a VC: import UIKit class NumpadViewController: UIViewController, UITextFieldDelegate { @IBOutlet weak var textField: UITextField! override func viewDidLoad() { super.viewDidLoad() textField.delegate = self textField.inputView = NumpadView(target: textField, view: view) } }
1
0
826
Aug ’22
How to avoid a double call of scrollViewDidScroll() method while swiping the collectionView?
I have created a User Onboarding as a  Collection View with 5 cells (pages). The Collection View has a UIPageControl which shows an active page user currently on and 2 UIButtons (previous and next) which needed to manually scroll the pages if user don't want to swipe. Here is how I manage the buttons IBAction when user tap: @IBAction func prevButtonClicked(_ sender: UIButton) { if currentPage != 0 { currentPage -= 1 let indexPath = IndexPath(item: currentPage, section: 0) collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true) } } @IBAction func nextButtonClicked(_ sender: UIButton) { if currentPage == slides.count - 1 { //hide onboarding } else { currentPage += 1 let indexPath = IndexPath(item: currentPage, section: 0) collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true) } } Also if user swipes a page instead of tap on buttons I use scrollViewDidScroll() method to update UIPageControl dot: func scrollViewDidScroll(_ scrollView: UIScrollView) { let visibleRectangle = CGRect(origin: collectionView.contentOffset, size: collectionView.bounds.size) let visiblePoint = CGPoint(x: visibleRectangle.midX, y: visibleRectangle.midY) currentPage = collectionView.indexPathForItem(at: visiblePoint)?.row ?? 0 } The currentPage is a computed property: private var currentPage = 0 { didSet { pageControl.currentPage = currentPage currentPage == 0 ? hidePreviousButton() : showPreviousButton() } } I have a problem: when tap on buttons I force collectionView to scroll and update currentPage, therefore scrollViewDidScroll called and currentPage updates again. Because of that when I tap on buttons I can see that UIPageControl dot and backButton are flicker since the code runs twice: didSet { pageControl.currentPage = currentPage currentPage == 0 ? hidePreviousButton() : showPreviousButton() } Here is a GIF with the problem: https://cln.sh/ffXG9A How can I avoid the double call to scrollViewDidScroll when tap on buttons?
0
0
1.3k
Apr ’22
How to format a number(text) in UITextField when user is typing?
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?
4
0
5.8k
Jul ’22
How to remove duplicate objects from CoreData in Swift?
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!
2
1
2.8k
Jan ’22