Skip to content

Commit 7cb366c

Browse files
committed
Add tip #58 and #59
1 parent 1329a7e commit 7cb366c

File tree

10 files changed

+182
-0
lines changed

10 files changed

+182
-0
lines changed

README.md

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ Here's list of Swift tips & tricks with all additional sources (playgrounds, ima
88

99
## 📃 Table of contents
1010

11+
[#59 `AlertPresentable` protocol]()<br />
12+
[#58 CollectionView extension for adaptive grid layout]()<br />
1113
[#57 Render HTML within a `UILabel`](https://github.com/Luur/SwiftTips#57-render-html-within-a-uilabel)<br />
1214
[#56 Custom `Error` by adopting `LocalizedError` protocol](https://github.com/Luur/SwiftTips#56-custom-error-by-adopting-localizederror-protocol)<br />
1315
[#55 'Result' type without value to provide](https://github.com/Luur/SwiftTips#55-result-type-without-value-to-provide)<br />
@@ -66,6 +68,99 @@ Here's list of Swift tips & tricks with all additional sources (playgrounds, ima
6668
[#2 Easy way to hide Status Bar](https://github.com/Luur/SwiftTips#2-easy-way-to-hide-status-bar)<br />
6769
[#1 Safe way to return element at specified index](https://github.com/Luur/SwiftTips#1-safe-way-to-return-element-at-specified-index)<br />
6870

71+
## [#59 `AlertPresentable` protocol]()
72+
73+
In my current project I work on I present alerts almost on every view controller. To reduce lines of codes and time spent on duplicate code I created `AlertPresentable` layer and want to share it with you.
74+
Any `ViewController` which implements `AlertPresentable` protocol receive opportunity to present any type of alerts discribed in this layer just by one line of code.
75+
76+
```swift
77+
protocol AlertPresentable {
78+
func presentErrorAlert(with message: String)
79+
func presentSuccessAlert(with message: String, action: ((UIAlertAction) -> Void)?)
80+
func presentConfirmationAlert(with message: String, action: ((UIAlertAction) -> Void)?)
81+
}
82+
83+
extension AlertPresentable where Self: UIViewController {
84+
85+
func presentErrorAlert(with message: String) {
86+
presentAlert(message: message, actions: [UIAlertAction(title: "OK", style: .cancel, handler: nil)])
87+
}
88+
89+
func presentSuccessAlert(with message: String, action: ((UIAlertAction) -> Void)?) {
90+
presentAlert(message: message, actions: [UIAlertAction(title: "OK", style: .cancel, handler: action)])
91+
}
92+
93+
func presentConfirmationAlert(with message: String, action: ((UIAlertAction) -> Void)?) {
94+
presentAlert(message: message, actions: [UIAlertAction(title: "Yes", style: .default, handler: action), UIAlertAction(title: "No", style: .cancel, handler: nil)])
95+
}
96+
97+
private func presentAlert(title: String? = "", message: String? = nil, actions: [UIAlertAction] = []) {
98+
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
99+
actions.forEach { (action) in
100+
alertController.addAction(action)
101+
}
102+
present(alertController, animated: true, completion: nil)
103+
}
104+
}
105+
```
106+
107+
Usage:
108+
109+
```swift
110+
class ViewController: UIViewController, AlertPresentable {
111+
112+
override func viewDidLoad() {
113+
super.viewDidLoad()
114+
115+
presentErrorAlert(with: "User not found")
116+
117+
presentSuccessAlert(with: "File downloaded") { _ in
118+
// use downloaded file
119+
}
120+
121+
presentConfirmationAlert(with: "Are you sure you would like to sign out?") { _ in
122+
// sign out user
123+
}
124+
}
125+
}
126+
```
127+
128+
Back to [Top](https://github.com/Luur/SwiftTips#-table-of-contents)
129+
130+
## [#58 CollectionView extension for adaptive grid layout]()
131+
132+
Implementation of grid CollectionView layout is a commont task. But I found that calculation of cell width when you dont know how many cells can fit in one row of CollectionView is not a common task.
133+
134+
Here is my extension for calculation width of cell in grid CollectionView to make your layot adaptive.
135+
136+
```swift
137+
extension UICollectionView {
138+
139+
func flexibleCellWidth(minCellWidth: CGFloat, minimumInteritemSpacing: CGFloat) -> CGFloat {
140+
let contentWidth = frame.size.width - contentInset.left - contentInset.right
141+
let numberOfItemsInRow = Int((contentWidth + minimumInteritemSpacing) / (minCellWidth + minimumInteritemSpacing))
142+
let spacesWidth = CGFloat(numberOfItemsInRow - 1) * minimumInteritemSpacing
143+
let availableContentWidth = contentWidth - spacesWidth
144+
return availableContentWidth / CGFloat(numberOfItemsInRow)
145+
}
146+
}
147+
```
148+
149+
Based on minimal cell width and minimal interitem spacing it autocalculates number of cells in row and return cell width for the best placement.
150+
151+
```swift
152+
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
153+
let cellWidth = collectionView.flexibleCellWidth(minCellWidth: 72, minimumInteritemSpacing: 10)
154+
return CGSize(width: cellWidth, height: cellWidth)
155+
}
156+
```
157+
158+
iPhoneSE ![](../master/Sources/58/img.png)
159+
160+
iPhoneX ![](../master/Sources/58/img1.png)
161+
162+
Back to [Top](https://github.com/Luur/SwiftTips#-table-of-contents)
163+
69164
## [#57 Render HTML within a `UILabel`]()
70165

71166
You can render HTML strings within a `UILabel` using a special initializer of `NSAttributedString` and passing in `NSAttributedString.DocumentType.html` for `.documentType`. But in most cases it is not enough to display it as is. If we want to add custom font or color we need to use CSS (add CSS header to our HTML string).
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import UIKit
2+
3+
extension UICollectionView {
4+
5+
func flexibleCellWidth(minCellWidth: CGFloat, minimumInteritemSpacing: CGFloat) -> CGFloat {
6+
let contentWidth = frame.size.width - contentInset.left - contentInset.right
7+
let numberOfItemsInRow = Int((contentWidth + minimumInteritemSpacing) / (minCellWidth + minimumInteritemSpacing))
8+
let spacesWidth = CGFloat(numberOfItemsInRow - 1) * minimumInteritemSpacing
9+
let availableContentWidth = contentWidth - spacesWidth
10+
return availableContentWidth / CGFloat(numberOfItemsInRow)
11+
}
12+
}
13+
14+
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
15+
let cellWidth = collectionView.flexibleCellWidth(minCellWidth: 72, minimumInteritemSpacing: 10)
16+
return CGSize(width: cellWidth, height: cellWidth)
17+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<playground version='5.0' target-platform='ios' executeOnSourceChanges='false'>
3+
<timeline fileName='timeline.xctimeline'/>
4+
</playground>

Sources/58/example.playground/playground.xcworkspace/contents.xcworkspacedata

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>IDEDidComputeMac32BitWarning</key>
6+
<true/>
7+
</dict>
8+
</plist>

Sources/58/img.png

90.6 KB
Loading

Sources/58/img1.png

85.6 KB
Loading
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import UIKit
2+
3+
protocol AlertPresentable {
4+
func presentErrorAlert(with message: String)
5+
func presentSuccessAlert(with message: String, action: ((UIAlertAction) -> Void)?)
6+
func presentConfirmationAlert(with message: String, action: ((UIAlertAction) -> Void)?)
7+
}
8+
9+
extension AlertPresentable where Self: UIViewController {
10+
11+
func presentErrorAlert(with message: String) {
12+
presentAlert(message: message, actions: [UIAlertAction(title: "OK", style: .cancel, handler: nil)])
13+
}
14+
15+
func presentSuccessAlert(with message: String, action: ((UIAlertAction) -> Void)?) {
16+
presentAlert(message: message, actions: [UIAlertAction(title: "OK", style: .cancel, handler: action)])
17+
}
18+
19+
func presentConfirmationAlert(with message: String, action: ((UIAlertAction) -> Void)?) {
20+
presentAlert(message: message, actions: [UIAlertAction(title: "Yes", style: .default, handler: action), UIAlertAction(title: "No", style: .cancel, handler: nil)])
21+
}
22+
23+
private func presentAlert(title: String? = "", message: String? = nil, actions: [UIAlertAction] = []) {
24+
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
25+
actions.forEach { (action) in
26+
alertController.addAction(action)
27+
}
28+
present(alertController, animated: true, completion: nil)
29+
}
30+
}
31+
32+
class ViewController: UIViewController, AlertPresentable {
33+
34+
override func viewDidLoad() {
35+
super.viewDidLoad()
36+
37+
presentErrorAlert(with: "User not found")
38+
39+
presentSuccessAlert(with: "File downloaded") { _ in
40+
// use downloaded file
41+
}
42+
43+
presentConfirmationAlert(with: "Are you sure you would like to sign out?") { _ in
44+
// sign out user
45+
}
46+
}
47+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<playground version='5.0' target-platform='ios' executeOnSourceChanges='false'>
3+
<timeline fileName='timeline.xctimeline'/>
4+
</playground>

0 commit comments

Comments
 (0)