For swiping in each direction we need to create Swipe gesture recognizer and specify direction that we need for this recognizer. For each direction we need create separate recognizer.
We can make code more universal. When we swiping in different directions we can handle all this swipes in one method and switch between directions.
Search This Blog
Monday, October 30, 2017
iOS Swift. How to detect double OR triple tap with UITapGestureRecognizer
For detecting touches in iOS application we use UITapGestureRecognizer. When we need react only on special number of touches, for example two or three, we must simply specify this parameter for gesture recognizer object.
When number of touches is 3
When number of touches is 2
Important to remember is make enable image for interaction with user.
Full code.
When number of touches is 3
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(imageTappedTriple)) tapGestureRecognizer.numberOfTapsRequired = 3
When number of touches is 2
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(imageTappedDouble)) tapGestureRecognizer.numberOfTapsRequired = 2
Important to remember is make enable image for interaction with user.
self.imageView.isUserInteractionEnabled = true
Full code.
import UIKit class ViewController: UIViewController { @IBOutlet var imageView: UIImageView! override func viewDidLoad() { super.viewDidLoad() let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(imageTappedTriple)) tapGestureRecognizer.numberOfTapsRequired = 3 self.imageView.isUserInteractionEnabled = true self.imageView.addGestureRecognizer(tapGestureRecognizer) } @objc func imageTappedTriple(sender: UITapGestureRecognizer) { print("Image was tapped three times!") } }
Sunday, October 29, 2017
How to add a border just to the one side of a UIView
We need to add border to UIView. Let's create view and place it in the center of screen with NSLayoutConstraint
var containerView: UIView! override func viewDidLoad() { super.viewDidLoad() self.containerView = UIView() self.containerView.backgroundColor = UIColor.red self.view.addSubview(containerView) self.containerView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ self.containerView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor), self.containerView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor), self.containerView.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 0.3), self.containerView.heightAnchor.constraint(equalTo: self.view.heightAnchor, multiplier: 0.3) ]) }
Border using SubView
In viewDidAppear method add top border to view using addSubView method.
override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) let topBorderView = UIView(frame: CGRect(x: 0, y: 0, width: self.containerView.frame.size.width, height: 10)) topBorderView.backgroundColor = UIColor.black self.containerView.addSubview(topBorderView) }Great, top border is added. But is it adaptive, what happen when parent view frame is changed. Let's see.
When parent view frame changed top border is not changed it's size.
What can go wrong when working with NSLayoutConstraint
Often when working with AutoLayout programmatically something can go wrong. Constraints that you created just not work or program fails. There are most popular mistakes that can happen
1. Forgot to addSubView method
You simply forgot to add your view as subview to parent view.
2. Forgot to set translatesAutoresizingMaskIntoConstraints property to false
Another important thing to remember. You need to enable AutoLayot for your view. So, if you forgot to set translatesAutoresizingMaskIntoConstraints to false then your view will not using AutoLayout.
3. Forgot to activate constraint
When you create new constraint it is not enough to make it work. You have to activate it.
4. Your views in different hierarchies
You can only add constraint to views that are in the same hierarchy. In other words - constraint for your view must operate with parent view (or view from the same hierarchy).
5. You must specify position and size both. Directly or indirectly
When you create constraints you need to specify size and position for your view. In other case your constraints will not work.
Directly specifying example (setup center position and width and height)
Indirectly specifying example (setup top, bottom, leading and width)
1. Forgot to addSubView method
You simply forgot to add your view as subview to parent view.
let containerView = UIView() self.view.addSubview(containerView)
2. Forgot to set translatesAutoresizingMaskIntoConstraints property to false
Another important thing to remember. You need to enable AutoLayot for your view. So, if you forgot to set translatesAutoresizingMaskIntoConstraints to false then your view will not using AutoLayout.
let containerView = UIView() containerView.translatesAutoresizingMaskIntoConstraints = false
3. Forgot to activate constraint
When you create new constraint it is not enough to make it work. You have to activate it.
containerView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
4. Your views in different hierarchies
You can only add constraint to views that are in the same hierarchy. In other words - constraint for your view must operate with parent view (or view from the same hierarchy).
5. You must specify position and size both. Directly or indirectly
When you create constraints you need to specify size and position for your view. In other case your constraints will not work.
Directly specifying example (setup center position and width and height)
containerView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true containerView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true containerView.widthAnchor.constraint(equalToConstant: 200).isActive = true containerView.heightAnchor.constraint(equalToConstant: 200).isActive = true
Indirectly specifying example (setup top, bottom, leading and width)
containerView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true containerView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true containerView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true containerView.widthAnchor.constraint(equalToConstant: 25).isActive = true
Monday, October 23, 2017
iOS Swift. How to Animate a Bar Button Item
Today I will show you how to make animated bar buttons and make it with coding. First of all we need icons for our navigation bar. Let's get them from google material design icons.
Google Material Design Icons
Adding icons to project
The only thing that we make with Storyboard is embed our view controller in navigation controller. We need it for navigation bar as container for bar buttons.
Declare two bar button items
And Bool variable for switch icon of favourite button.
Creating images from Assets. One image for settings icon and two images for favourite icon (favourite and unfavourite).
Inside viewDidLoad method let's create UIButton, configure it and initialize settings bar button item with this UIButton. We put it to the left part of navigation item of view controller.
It is touch handler for button. Here we make animation. There are two steps:
The same for favourite bar button. Initially we create it with favourite empty icon (unfavourite). We put it to the right part of navigation item of view controller.
GitHub Link
Google Material Design Icons
Adding icons to project
The only thing that we make with Storyboard is embed our view controller in navigation controller. We need it for navigation bar as container for bar buttons.
Declare two bar button items
var settingsBarButton: UIBarButtonItem? var favoriteBarButton: UIBarButtonItem?
And Bool variable for switch icon of favourite button.
var favorite: Bool = false
Creating images from Assets. One image for settings icon and two images for favourite icon (favourite and unfavourite).
let settingsImage = UIImage(named: "ic_settings_48pt")?.withRenderingMode(.alwaysTemplate) let favoriteBorderImage = UIImage(named: "ic_favorite_border_48pt")?.withRenderingMode(.alwaysTemplate) let favoriteFullImage = UIImage(named: "ic_favorite_48pt")?.withRenderingMode(.alwaysTemplate)
Inside viewDidLoad method let's create UIButton, configure it and initialize settings bar button item with this UIButton. We put it to the left part of navigation item of view controller.
let settingsButton = UIButton(type: .system) settingsButton.tintColor = .black settingsButton.setImage(self.settingsImage, for: .normal) settingsButton.frame = CGRect(x: 0, y: 0, width: 30, height: 30) settingsButton.addTarget(self, action: #selector(settingsButtonTapped), for: .touchUpInside) self.settingsBarButton = UIBarButtonItem(customView: settingsButton) self.navigationItem.setLeftBarButton(settingsBarButton, animated: false)
It is touch handler for button. Here we make animation. There are two steps:
- Rotate view to some angle
- With animation we restore initial view rotation as it was before first rotation
@objc func settingsButtonTapped(_ sender: UIButton) { self.settingsBarButton?.customView?.transform = CGAffineTransform(rotationAngle: CGFloat(CGFloat.pi * -3/4)) UIView.animate(withDuration: 0.8) { self.settingsBarButton?.customView?.transform = .identity } }
The same for favourite bar button. Initially we create it with favourite empty icon (unfavourite). We put it to the right part of navigation item of view controller.
let favoriteButton = UIButton(type: .system) favoriteButton.tintColor = .black favoriteButton.setImage(self.favoriteBorderImage, for: .normal) favoriteButton.frame = CGRect(x: 0, y: 0, width: 30, height: 30) favoriteButton.addTarget(self, action: #selector(favoriteButtonTapped), for: .touchUpInside) self.favoriteBarButton = UIBarButtonItem(customView: favoriteButton) self.navigationItem.setRightBarButton(favoriteBarButton, animated: false)
Touch handler for favourite button. The same logic as before, but here we use other type of transformation(previously we use rotation), now we use scaling transformation:
- Change button scale.
- Restore button scale with animation. Also inside animation we change icon of button.
Here we use spring animation. It means that here will be appear special effects during animations. Such as velocity and damping changing and etc.
@objc func favoriteButtonTapped(_ sender: UIButton) { self.favoriteBarButton?.customView?.transform = CGAffineTransform(scaleX: 0, y: 0) UIView.animate(withDuration: 0.5, delay: 0.0, usingSpringWithDamping: 0.6, initialSpringVelocity: 10, options: .curveEaseInOut, animations: { self.favorite = !self.favorite let image = self.favorite ? self.favoriteFullImage : self.favoriteBorderImage if let button = self.favoriteBarButton?.customView as? UIButton { button.setImage(image, for: .normal) } self.favoriteBarButton?.customView?.transform = .identity }, completion: nil) }
How it all work
GitHub Link
Sunday, October 22, 2017
How to remove all subviews of a view in Swift?
Following task - remove all subViews of current view. Let's see how we can implement this.
For example we create UIView and add UILabel to it.
1. Simple For Loop
2. ForEach Loop
3. Using Map Function
4. Making Extension
If we want to make removing enable for all views in project then let's create Extension.
5. Going Universal with Generics
We can go further and make our Extension universal with Generics. Now You can specify which type of subViews You want to remove.
6. The Same but Using Filter and Map Functions
The same as above. But here we use chain of High-order functions.
For example we create UIView and add UILabel to it.
var containerView = UIView() containerView.addSubview(UILabel(frame: CGRect.zero))
1. Simple For Loop
for subView in containerView.subviews { subView.removeFromSuperview() }
2. ForEach Loop
containerView.subviews.forEach { $0.removeFromSuperview() }
3. Using Map Function
containerView.subviews.map { $0.removeFromSuperview() }
4. Making Extension
extension UIView { func removeAllSubView() { self.subviews.forEach { $0.removeFromSuperview() } } } containerView.removeAllSubView()
5. Going Universal with Generics
We can go further and make our Extension universal with Generics. Now You can specify which type of subViews You want to remove.
extension UIView { func removeAllSubViewOfType<T: UIView>(type: T.Type) { self.subviews.forEach { if ($0 is T) { $0.removeFromSuperview() } } } } containerView.removeAllSubViewOfType(type: UILabel.self)
6. The Same but Using Filter and Map Functions
The same as above. But here we use chain of High-order functions.
extension UIView { func removeAllSubViewOfTypeUsingHOF<T: UIView>(type: T.Type) { self.subviews.filter({ $0 is T }).map({ $0.removeFromSuperview() }) } } containerView.removeAllSubViewOfTypeUsingHOF(type: UILabel.self)
Thursday, October 19, 2017
Flat nested array recursively in Swift
Following task - make flat array from nested array. Because there are could be any levels of nested array we use recursive approach.
For example - given array [1, [2, [3, 4, 5]]]. We need to get this [1, 2, 3, 4, 5]
Standard Approach
Algorithm
Let's get coding.
Using Generics
What if we want to use this function with array of any type. We should use Generics:
Now we can use this function with any kind of array. With integer array as previous
And with array of strings
Make Extension for Array
What if we want this feature for any array as built in method. Let's create extension for Array Type. Here we use Swift method flatMap.
Result of using array extension.
For example - given array [1, [2, [3, 4, 5]]]. We need to get this [1, 2, 3, 4, 5]
Standard Approach
Algorithm
- In for loop go through the array
- If element of array is int then add it to new array
- If element of array is array too then make recursive invoke of function and send this array
Let's get coding.
import UIKit let array: [Any] = [1, 2, [3]] func makeFlatArray(_ array: [Any]) -> [Int] { var flatArray = [Int]() for item in array { if let item = item as? Int { flatArray.append(item) } else if let item = item as? [Any] { let result = makeFlatArray(item) flatArray += result } } return flatArray } print(makeFlatArray([1, 2, 3])) // [1, 2, 3] print(makeFlatArray([1, [2, 3, 4]])) // [1, 2, 3, 4] print(makeFlatArray([1, [2, [3, 4]]])) // [1, 2, 3, 4] print(makeFlatArray([[1], [2, [3, 4, [5]]]])) // [1, 2, 3, 4, 5]
Using Generics
What if we want to use this function with array of any type. We should use Generics:
func makeFlatArrayGeneric<T>(_ array: [Any]) -> [T] { var flatArray = [T]() for item in array { if let item = item as? T { flatArray.append(item) } else if let item = item as? [Any] { let result: [T] = makeFlatArrayGeneric(item) flatArray += result } } return flatArray }
Now we can use this function with any kind of array. With integer array as previous
let array: [Any] = [1, 2, [3], [4, [5]]] let items: [Int] = makeFlatArrayGeneric(array) // [1, 2, 3, 4, 5]
And with array of strings
let array: [Any] = ["A", "BB", ["CCC"], ["DD", ["EE"]]] let items: [String] = makeFlatArrayGeneric(array) // ["A", "BB", "CCC", "DD", "EE"]
Make Extension for Array
What if we want this feature for any array as built in method. Let's create extension for Array Type. Here we use Swift method flatMap.
extension Array { func makeFlat() -> [Element] { let flatArray = self.flatMap { (element) -> [Element] in if let array = element as? Array { return array.makeFlat() } return [element] } return flatArray } }
Result of using array extension.
let array: [Any] = ["A", "BB", ["CCC"], ["DD", ["EE"]]] print(array.makeFlat()) // ["A", "BB", "CCC", "DD", "EE"]
Tuesday, October 17, 2017
Reverse every even or every odd word in string in swift
Following task - reverse every even or every odd word in sentence.
Algorithm
But we can improve our code with using High-ordered function.
There is following algorithm:
1. Create array of string from initial string
2. Convert this array to enumerated. According to Apple Documentation after this we can get sequence of pairs with index and value.
Algorithm
- Get array of words from string (sentence)
- Create new empty string
- In for loop go through the array and according to condition reverse word or not and append it to new string
import UIKit let str = "This is the string which will be reverted particaually" enum WhichNumer: Int { case Even = 0 case Odd = 1 } func reverseWords(str: String, number: WhichNumer = .Even) -> String { let array = str.components(separatedBy: " ") var newStr = "" for i in 0..<array.count { if newStr != "" { newStr += " " } let word = array[i] if (i + 1) % 2 == number.rawValue { newStr += String(word.reversed()) } else { newStr += word } } return newStr } print(reverseWords(str: str)) // This si the gnirts which lliw be detrever particaually print(reverseWords(str: str, number: .Odd)) //sihT is eht string hcihw will eb reverted yllauacitrap
But we can improve our code with using High-ordered function.
There is following algorithm:
1. Create array of string from initial string
str.components(separatedBy: " ") // ["This", "is", "the", "string", "which", "will", "be", "reverted", "particaually"]
2. Convert this array to enumerated. According to Apple Documentation after this we can get sequence of pairs with index and value.
"Returns a sequence of pairs (n, x), where n represents a consecutive integer starting at zero, and x represents an element of the sequence."
3. Now we can use map function to this array of pairs. We check index and modify value if needed.
4. Finally, array of words can be convert to string with joined function.
Here new implementation of our function.
str.components(separatedBy: " ").enumerated() // 0 : This // 1 : is // 2 : the // 3 : string // 4 : which // 5 : will // 6 : be // 7 : reverted // 8 : particaually
3. Now we can use map function to this array of pairs. We check index and modify value if needed.
str.components(separatedBy: " ").enumerated().map { (index, value) -> String in if ((index + 1) % 2 == number.rawValue) { return String(value.reversed()) } return value }) // ["This", "si", "the", "gnirts", "which", "lliw", "be", "detrever", "particaually"]
4. Finally, array of words can be convert to string with joined function.
let newStr = str.components(separatedBy: " ").enumerated().map { (index, value) -> String in if ((index + 1) % 2 == number.rawValue) { return String(value.reversed()) } return value }.joined(separator: " ") // This si the gnirts which lliw be detrever particaually
Here new implementation of our function.
func reverseWordsUsingHOF(str: String, number: WhichNumer = .Even) -> String { let newStr = str.components(separatedBy: " ").enumerated().map { (index, value) -> String in print(index) if ((index + 1) % 2 == number.rawValue) { return String(value.reversed()) } return value }.joined(separator: " ") return newStr } print(reverseWordsUsingHOF(str: str)) // This si the gnirts which lliw be detrever particaually
Subscribe to:
Posts (Atom)