Post

Replies

Boosts

Views

Activity

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
654
Aug ’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
4.9k
Feb ’22
How to insert a character at any cursor position in a textField?
I have a textField where I can type numbers (2 333,44) or simple math equations (12+3, 11x3,5). For now I can modify the textField.text with adding a new string only to the end of existing text. If I move cursor to any position inside number it will add a new character only at the end: https://i.stack.imgur.com/HzaA9.gif How to modify the code to insert new characters at any cursor position? My current code from shouldChangeCharactersIn: func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { let formatter = NumberFormatter() formatter.numberStyle = .decimal formatter.maximumFractionDigits = 2 formatter.minimumFractionDigits = 1 formatter.locale = .current formatter.roundingMode = .down let numberString = "\(textField.text ?? "")".replacingOccurrences(of: formatter.groupingSeparator, with: "") let lastCharacter = numberString.last ?? "." var symbol: String { var tempArray = [String]() let mathSymbols = "+-÷x" for character in numberString { if mathSymbols.contains(character) { tempArray.append(String(character)) } } return tempArray.last ?? "" } var numbersArray: [String] { if numberString.first == "-" { let positiveString = numberString.dropFirst() var tempArray = positiveString.components(separatedBy: symbol) tempArray[0] = "-\(tempArray[0])" return tempArray } else { return numberString.components(separatedBy: symbol) } } //turn numbers into Float and create a String from them to be able to receive a correct result from NSExpression var floatNumberArray: [Float] { if numberString.first == "-" { let tempString = lastCharacter == Character(formatter.decimalSeparator) ? numberString.dropFirst().dropLast() : numberString.dropFirst() var tempArray = tempString.components(separatedBy: symbol) tempArray[0] = "-\(tempArray[0])" return tempArray.compactMap { Float($0.replacingOccurrences(of: formatter.decimalSeparator, with: ".")) } } else { return numberString.components(separatedBy: symbol).compactMap { Float($0.replacingOccurrences(of: formatter.decimalSeparator, with: ".")) } } } var floatNumberString: String { if numberString.contains("x") { return "\(floatNumberArray.first ?? 0)\("*")\(floatNumberArray.last ?? 0)" } else if numberString.contains("÷") { return "\(floatNumberArray.first ?? 0)\("/")\(floatNumberArray.last ?? 0)" } else { return "\(floatNumberArray.first ?? 0)\(symbol)\(floatNumberArray.last ?? 0)" } } let amountOfDecimals = "\(numbersArray.last ?? "")\(string)".filter({ $0 == Character(formatter.decimalSeparator) }).count //allow to insert 0 after the decimal symbol to avoid formatting i.e. 2.04 formatter.minimumFractionDigits = numberString.last == Character(formatter.decimalSeparator) && string == "0" ? 1 : 0 //allow string to be modified by backspace button if string == "" { return false } //allow numbers as a first character, except math symbols if numberString == "" { if Character(string).isNumber { textField.text = string } else { return false } } //allow only one decimal symbol per number else if amountOfDecimals > 1 { return false } if numbersArray.count > 1 { //if number is entered if Character(string).isNumber { textField.text = "\(formatter.string(for: Double("\(numbersArray.first?.replacingOccurrences(of: formatter.decimalSeparator, with: ".") ?? "")") ?? 0) ?? "")\(symbol)\(formatter.string(for: Double("\(numbersArray.last?.replacingOccurrences(of: formatter.decimalSeparator, with: ".") ?? "")\(string)") ?? 0) ?? "")" //if symbol is entered } else if string == formatter.decimalSeparator { textField.text = "\(textField.text ?? "")\(string)" } else { //perform calculation if last entered character is a number if lastCharacter.isNumber { let result = performCalculation(format: floatNumberString) textField.text = string == "=" ? "\(formatter.string(from: result) ?? "")" : "\(formatter.string(from: result) ?? "")\(string)" //perform calculation if last entered character is a decimal symbol } else if lastCharacter == Character(formatter.decimalSeparator) { let result = performCalculation(format: floatNumberString) textField.text = string == "=" ? "\(formatter.string(from: result) ?? "")" : "\(formatter.string(from: result) ?? "")\(string)" //change math symbol before enter a second number } else { textField.text = "\(textField.text?.dropLast() ?? "")\(string)" } } } else { //if number is entered if Character(string).isNumber { textField.text = "\(formatter.string(for: Double("\(numbersArray.first?.replacingOccurrences(of: formatter.decimalSeparator, with: ".") ?? "")\(string)") ?? 0) ?? "")" } else { //if math symbol is entered if lastCharacter.isNumber { textField.text = "\(textField.text ?? "")\(string)" } } } return false } func performCalculation(format: String) -> NSNumber { let expression = NSExpression(format: format) let answer = expression.expressionValue(with: nil, context: nil) return answer as! NSNumber } } My guess is to modify the string with a range. So I tried: 1.Create range let numberString = "\(textField.text ?? "")".replacingOccurrences(of: formatter.groupingSeparator, with: "") guard let range = Range(range, in: numberString) else { return false } 2.Remove string at the end and use range instead } else { //if number is entered if Character(string).isNumber { textField.text = "\(formatter.string(for: Double("\(numbersArray.first?.replacingOccurrences(of: formatter.decimalSeparator, with: ".").replacingCharacters(in: range, with: string) ?? "") ?? "")") ?? 0) ?? "")" } else { //if math symbol is entered if lastCharacter.isNumber { textField.text = "\(textField.text?.replacingCharacters(in: range, with: string) ?? "") ?? "")" } } } return false } 3.Then I can insert at any position but can add only 4 numbers if add from keyboard and insert more from another cursor position: https://i.stack.imgur.com/iDV4n.gif Test project on GitHub
0
0
1.1k
Jul ’22
How to replace a specific character in a string inside a UITextField?
I have a custom textField's input view - it is a Numpad style keyboard. Numpad is using to add numbers and math symbols to a textField. I can't figure out how can I change a math symbol in a string if user already add one and wants to change it on another straight away. Here is an example of what I need: https://i.stack.imgur.com/IVGId.gif Here is the code I use: func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { //number formatter let formatter = NumberFormatter() formatter.numberStyle = .decimal formatter.maximumFractionDigits = 2 formatter.locale = .current formatter.roundingMode = .down //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 } //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 } //count how much decimals string has. If more that one - do not insert because it can have only one per number let numbersArray = completeString.components(separatedBy: symbol) for number in numbersArray { let amountOfDecimalSigns = number.filter({$0 == "."}).count if amountOfDecimalSigns > 1 { return false } } //create numbers from a string guard let firstNumber = Double(String(numbersArray.first ?? "0")) else { return true } guard let secondNumber = Double(String(numbersArray.last ?? "0")) else { return true } //format numbers and turn them back to string let firstFormattedNumber = formatter.string(for: firstNumber) ?? "" let secondFormattedNumber = formatter.string(for: secondNumber) ?? "" //assign formatted numbers to a textField textField.text = completeString.contains(symbol) ? "\(firstFormattedNumber)\(symbol)\(secondFormattedNumber)" : "\(firstFormattedNumber)" return string == formatter.decimalSeparator } The logic for me was to use textField.deleteBackwards() method to delete an old one and add a new math symbol after, but with above code it doesn't work: it deletes symbol, but a new one doesn't appear - I should press again so new symbol can appear. What should I do to change a math symbol in a string? Test project on GitHub
4
0
724
Jul ’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
683
Jul ’22
How to format numbers in UITextField after input from custom keyboard view?
My app used to rely on standard iOS Numpad Keyboard as a textField.inputView. The textField could group typed numbers (1000 -> 1 000, 50067 -> 50 067 etc) and limit the amount of numbers after the decimal point as 2 max: https://i.stack.imgur.com/69LVr.gif (GIF) I've created a custom numpad keyboard view and implement its logic with UITextInput protocol according to that guide on StackOverflow. I can't understand of how to deal with the code I used for grouping and limit numbers with standard iOS Numpad. Because it doesn't work properly after I type numbers from my new custom Numpad (only 2 numbers): https://i.stack.imgur.com/f3u3n.gif (GIF) The code I use for grouping and limit numbers after decimal point in a textField: func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { let formatter = NumberFormatter() formatter.numberStyle = .decimal formatter.groupingSeparator = " " formatter.maximumFractionDigits = 2 let textString = textField.text ?? "" guard let range = Range(range, in: string) 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: "") guard completeString.count <= 12 else { return false } guard !completeString.isEmpty else { return true } textField.text = completeString return string == formatter.decimalSeparator } Custom NumpadView: class NumpadView: UIView { var target: UITextInput? init(target: UITextInput) { super.init(frame: .zero) self.target = target 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] } @IBAction func buttonPressed(_ sender: NumpadButton) { insertText((sender.titleLabel!.text)!) } func insertText(_ string: String) { guard let range = target?.selectedRange else { return } if let textField = target as? UITextField, textField.delegate?.textField?(textField, shouldChangeCharactersIn: range, replacementString: string) == false { return } target?.insertText(string) } } extension UITextInput { var selectedRange: NSRange? { guard let selectedRange = selectedTextRange else { return nil } let location = offset(from: beginningOfDocument, to: selectedRange.start) let length = offset(from: selectedRange.start, to: selectedRange.end) return NSRange(location: location, length: length) } } 2.How I initialize it in a VC: override func viewDidLoad() { super.viewDidLoad() textField.delegate = self textField.inputView = NumpadView(target: textField) } Test project on GitHub: CLICK What should I do so the code in shouldChangeCharactersIn method work for my custom Numpad?
0
0
420
Jul ’22
How to show a Large Title when scroll view up?
I have implemented a feature, when you press on a UITabBar icon and viewController1 scrolls up using its UIScrollView. It works perfectly, but if I scroll view down and stop somewhere, then switch to another viewController2, then get back to viewController1 and press tabBar icon - the viewController1 will scroll up, but Large Title will never be showed, and I should press tabBar icon one more time to show it: Here is a GIF with behaviour: https://i.stack.imgur.com/VfP4o.gif The code I use for scroll up the VC1: private var biggestTopSafeAreaInset: CGFloat = 0 override func viewSafeAreaInsetsDidChange() { super.viewSafeAreaInsetsDidChange() self.biggestTopSafeAreaInset = max(view.safeAreaInsets.top, biggestTopSafeAreaInset) } func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) { if tabBarController.selectedIndex == 0 { let navigationVC = viewController as? UINavigationController let firstVC = navigationVC?.viewControllers.first as? CurrencyViewController guard let scrollView = firstVC?.view.subviews.first(where: { $0 is UIScrollView }) as? UIScrollView else { return } if traitCollection.verticalSizeClass == .compact { scrollView.setContentOffset(CGPoint(x: 0, y: -view.safeAreaInsets.top, animated: true) } else { scrollView.setContentOffset(CGPoint(x: 0, y: -biggestTopSafeAreaInset, animated: true) } } } I tried to track biggestTopSafeAreaInset in different stages of VC1 life, but it always has the same number - 196.0. But then why it doesn't scroll till the Large Title after viewControllers switch?
0
0
410
Jun ’22
How to check if one of URLSession tasks returned an error and if so to stop code execution?
I need to make 2 API calls simultaneously. I have 2 URLs for the calls, and if one of the calls will return any error I want to stop all the code execution. How I tried to do it: I have a function called performRequest() with a completion block. I call the function in my ViewController to update the UI - show an error/or a new data if all was successful. Inside it I create a URLSession tasks and then parse JSON: I created an array with 2 urls: func performRequest(_ completion: @escaping (Int?) -> Void) { var urlArray = [URL]() guard let urlOne = URL(string: "https://api.exchangerate.host/latest?base=EUR&places=9&v=1") else { return } guard let urlTwo = URL(string: "https://api.exchangerate.host/2022-05-21?base=EUR&places=9") else { return } urlArray.append(urlOne) urlArray.append(urlTwo) } Then for each of the url inside the array I create a session and a task: urlArray.forEach { url in let session = URLSession(configuration: .ephemeral) let task = session.dataTask(with: url) { data, _, error in if error != nil { guard let error = error as NSError? else { return } completion(error.code) return } if let data = data { let printData = String(data: data, encoding: String.Encoding.utf8) print(printData!) DispatchQueue.main.async { self.parseJSON(with: data) } } } task.resume() } print("all completed") completion(nil) } For now I receive print("all completed") printed once in any situation: if both tasks were ok, if one of them was ok or none of them. What I want is to show the print statement only if all tasks were completed successfully and to stop executing the code if one of them returned with error (for example if we will just delete one of the symbols in url string which will take it impossible to receive a data). How can I do it correctly?
0
0
328
May ’22
How to reload collectionView cell when it did end displaying?
I have created a User Onboarding as a Collection View with 5 cells (pages). I have 2 types of Collection View Items: welcomeCell which is always a first and single cell in Onboarding (indexPath.item == 0) and tutorialCell (all other cells) which has a tableView and can scroll its content vertically. I want to add the next behaviour: user scrolls tableView content and swipes to the next page if user swipes back to the page he scrolled I want the page layout to be reloaded like it was initially set. For now if a user swipes to the page he scrolled, he will see its position where he ended the scroll. I suggests that collectionView content can be reloaded in didEndDisplaying method: func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { if indexPath.item != 0 { self.collectionView.reloadItems(at: [indexPath]) } } But with this code I receive a strange cells behaviour: I can swipe up tableView on one page and the came content position will be on another page, but some page can be loaded with normal content position. Here is a GIF: https://cln.sh/wgnjS8 I also tried collectionView.reloadData() in scrollViewDidScroll and scrollViewDidEndDecelerating but receive the similar behaviour. Would you be so kind to share some experience and help to understand what's the best practice for my case? Maybe I should find a way to not reload a page content at all, but reload imageView height anchor in constraints?
0
0
1.6k
Apr ’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
1k
Apr ’22
Fix a CollectionView item animation while changing a device orientation
I have made an Onboarding View using a collectionView with 2 items. When I change device orientation from landscape to portrait the second item being animated and visible within the first item screen borders. You can see the problem here: https://cln.sh/sgypvH How to fix the animation so when switching from landscape to portrait I could see only the first item content with a white background on the screen? Here is my code for collectionViewLayout: func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { return CGSize(width: collectionView.frame.width, height: collectionView.frame.height) } Here is the code for willTransition:     override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {         collectionView.collectionViewLayout.invalidateLayout()           let indexPath = IndexPath(item: self.currentPage, section: 0)         DispatchQueue.main.async {             self.collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)         }     } Please let me know if you need anything extra to help me to solve the issue. Thank you!
0
0
363
Apr ’22
Can't combine UIImageView rotation animation and tableView section reload
I have 4 sections, each section have 2 nested rows. I open the rows by tapping on each section. Here is how my initial data looks like. It has title, subtitle and options (which is what nested rows should display): private var sections = [ SortingSection(title: "По имени", subtitle: "Российский рубль", options: ["По возрастанию (А→Я)", "По убыванию (Я→А)"]), SortingSection(title: "По короткому имени", subtitle: "RUB", options: ["По возрастанию (А→Я)", "По убыванию (Я→А)"]), SortingSection(title: "По значению", subtitle: "86,22", options: ["По возрастанию (1→2)", "По убыванию (2→1)"]), SortingSection(title: "Своя", subtitle: "в любом порядке", options: ["Включить"]) ] When I tap on a section I want it accessory (chevron.right, made as UIImageView) be rotated in sync with expanding of nested rows and when I click again the same behaviour for closing. I have a variable called isOpened (bool, false by default), which I change from false to true and back each tap in didSelectRowAt. Based on that a show all nested cells and rotate the UIImageView: override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if indexPath.row == 0 { sections[indexPath.section].isOpened.toggle() guard let cell = tableView.cellForRow(at: indexPath) as? MainSortTableViewCell else { return } UIView.animate(withDuration: 0.3) { if self.sections[indexPath.section].isOpened { cell.chevronImage.transform = CGAffineTransform(rotationAngle: .pi/2) } else { cell.chevronImage.transform = .identity } } completion: { _ in tableView.reloadSections([indexPath.section], with: .none) } } As you can see above I reload tableView section to show\hide nested rows in a completion block after animation. I can't use reloadSections in an if\else statement because then chevron animation gets skipped. Also my numberOrRowsInSection method: override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { let section = sections[section] if section.isOpened { return section.options.count + 1 } else { return 1 } } Here is how it looks now: https://cln.sh/3hjI2q Here is what I want (any iPhone native apps): https://cln.sh/z0LM6E I tried to add and delete rows instead of reloading the whole section, but always end up with error: UIView.animate(withDuration: 0.3) { if self.sections[indexPath.section].isOpened { cell.chevronImage.transform = CGAffineTransform(rotationAngle: .pi/2) for i in 0..<self.sections[indexPath.section].options.count { tableView.insertRows(at: [IndexPath(row: 1+i, section: indexPath.section)], with: .none) } } else { cell.chevronImage.transform = .identity for i in 0..<self.sections[indexPath.section].options.count { tableView.deleteRows(at: [IndexPath(row: i-1, section: indexPath.section)], with: .none) } } } How can I change my code to solve the task and animate chevron at the same time nested rows expand or close?
0
0
418
Apr ’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
670
Mar ’22
Can't add a UIView loaded from XIB as a subView of a ViewController view
I want to implement a popup notification into my app when data was being updated successfully or not. To do that I: created a .xib file: Screenshot - https://cln.sh/HvEHiX created a class where I load that NIB: import UIKit class PopupView: UIView { static let instance = PopupView() @IBOutlet weak var backgroundView: UIView! @IBOutlet weak var popupView: UIVisualEffectView! @IBOutlet weak var symbol: UIImageView! @IBOutlet weak var titleLabel: UILabel! @IBOutlet weak var descriptionLabel: UILabel! override init(frame: CGRect) { super.init(frame: frame) Bundle.main.loadNibNamed("PopupView", owner: self) popupView.layer.cornerRadius = 20 } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func showPopup(title: String, message: String, symbol: UIImage, on viewController: UIViewController) { self.titleLabel.text = title self.descriptionLabel.text = message self.symbol.image = symbol guard let targetView = viewController.view else { return } backgroundView.frame = targetView.bounds targetView.addSubview(backgroundView) } In the above class I created a showPopup method where defined a backgroundView frame to be equal to ViewController bounds. When I call that method in desired ViewController I receive the behaviour where my popupView shows itself and then went off the screen straight away (black area in the GIF): GIF - https://cln.sh/e4ukf4 Would you be so kind to help me understand and fix the reason why the popupView went off the screen and not just equal to a ViewController bounds.
3
0
975
Mar ’22
Ambiguous Layout: Position and size are ambiguous for "Stack View"
I am building a Settings screen using Storyboard and UITableViewController with Static Cells. Here is a full view hierarchy and constraints I use for every cell in my Settings Table View: https://cln.sh/tAmFbl The problem appears when I switch portrait mode on landscape mode or switch to device with a smaller screen. Starting to receive an errors like: Ambiguous Layout: Position and size are ambiguous for "Stack View". And: Stack View, need constraints for: X position, width Stack View, need constraints for: Y position, height Here is a picture of the behaviour: https://cln.sh/fq6kOK If I switch back to portrait mode all errors goes away... How can I fix that?
1
0
2.8k
Feb ’22