├── .gitignore
├── LICENSE
├── README.md
├── Swift2-ProtocolExtensions-ErrorRendering.playground
├── Contents.swift
├── contents.xcplayground
├── playground.xcworkspace
│ └── contents.xcworkspacedata
└── timeline.xctimeline
├── example.gif
└── example.mov
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | build/
4 | *.pbxuser
5 | !default.pbxuser
6 | *.mode1v3
7 | !default.mode1v3
8 | *.mode2v3
9 | !default.mode2v3
10 | *.perspectivev3
11 | !default.perspectivev3
12 | xcuserdata
13 | *.xccheckout
14 | *.moved-aside
15 | DerivedData
16 | *.hmap
17 | *.ipa
18 | *.xcuserstate
19 |
20 | # CocoaPods
21 | #
22 | # We recommend against adding the Pods directory to your .gitignore. However
23 | # you should judge for yourself, the pros and cons are mentioned at:
24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
25 | #
26 | #Pods/
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Jeff Hurray
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Swift 2.0 Protocol Extension Example
2 | Playground showing how to use Swift2 protocol extensions to render errors in UIViews and UIViewControllers without subclassing or creating classes
3 |
4 |
5 |
6 | **UIViewControllers** will render an error as a toast banner
7 |
8 | **UIViews** will render an error by adding a generic error label to the views top right corner.
9 |
10 | This was inspired by a [sweet article](http://krakendev.io/blog/subclassing-can-suck-and-heres-why) by [KrakenDev](http://krakendev.io)
11 |
12 |
13 |
14 | ```swift
15 |
16 | import UIKit
17 | import XCPlayground
18 |
19 | struct ErrorOptions {
20 | let message: String
21 | let tintColor: UIColor
22 |
23 | init(message: String = "Error!", tintColor: UIColor = UIColor.clearColor()) {
24 | self.message = message
25 | self.tintColor = tintColor
26 | }
27 | }
28 |
29 | typealias ErrorRenderingCompletionBlock = ()->()
30 |
31 | protocol ErrorPopoverRenderer {
32 | func presentError(errorOptions: ErrorOptions)
33 | func presentError(errorOptions: ErrorOptions, completion : ErrorRenderingCompletionBlock?)
34 | }
35 |
36 | extension ErrorPopoverRenderer {
37 | func presentError(errorOptions: ErrorOptions = ErrorOptions()) {
38 | self.presentError(errorOptions, completion: nil)
39 | }
40 | }
41 |
42 | extension ErrorPopoverRenderer where Self: UIViewController {
43 |
44 | func presentError(errorOptions: ErrorOptions = ErrorOptions(), completion : ErrorRenderingCompletionBlock?) {
45 | let errorBanner = UILabel()
46 | errorBanner.backgroundColor = errorOptions.tintColor
47 | errorBanner.textAlignment = .Center
48 | errorBanner.adjustsFontSizeToFitWidth = true
49 | errorBanner.font = UIFont.systemFontOfSize(20.0)
50 | errorBanner.textColor = UIColor.whiteColor()
51 | errorBanner.text = errorOptions.message
52 | let height : CGFloat = 50
53 | errorBanner.frame = CGRect(x: 0, y: -height, width: CGRectGetWidth(self.view.bounds), height: height)
54 | self.view.addSubview(errorBanner)
55 | UIView.animateWithDuration(0.8, animations: { () -> Void in
56 | errorBanner.transform = CGAffineTransformMakeTranslation(0, height)
57 | }) { (done1) -> Void in
58 | UIView.animateWithDuration(0.8, delay: 0.5, options: UIViewAnimationOptions.CurveEaseOut, animations: { () -> Void in
59 | errorBanner.transform = CGAffineTransformIdentity
60 | }, completion: { (done2) -> Void in
61 | errorBanner.removeFromSuperview()
62 | if let completionBlock = completion {
63 | completionBlock()
64 | }
65 | })
66 | }
67 | }
68 | }
69 |
70 | extension ErrorPopoverRenderer where Self: UIView {
71 |
72 | func presentError(errorOptions: ErrorOptions = ErrorOptions(), completion : ErrorRenderingCompletionBlock?) {
73 | let errorBanner = UILabel()
74 | errorBanner.backgroundColor = errorOptions.tintColor
75 | errorBanner.textAlignment = .Center
76 | errorBanner.adjustsFontSizeToFitWidth = true
77 | errorBanner.font = UIFont.systemFontOfSize(18.0)
78 | errorBanner.text = "!"
79 | errorBanner.textColor = UIColor.redColor()
80 | let size : CGFloat = 32.0
81 | let padding : CGFloat = 8.0
82 | errorBanner.layer.cornerRadius = size/2.0
83 | errorBanner.layer.borderColor = UIColor.redColor().CGColor
84 | errorBanner.layer.borderWidth = 1.0
85 | errorBanner.frame = CGRect(x: CGRectGetWidth(self.bounds) - size - padding, y: padding, width: size, height: size)
86 | self.addSubview(errorBanner)
87 | if let completionBlock = completion {
88 | completionBlock()
89 | }
90 | }
91 | }
92 |
93 | extension UIViewController : ErrorPopoverRenderer {}
94 | extension UIView : ErrorPopoverRenderer {}
95 |
96 |
97 | let viewController = UIViewController()
98 | viewController.view.frame = CGRect(x: 0, y: 0, width: 375, height: 667)
99 | viewController.view.backgroundColor = UIColor.whiteColor()
100 | XCPShowView("Controller View", view: viewController.view)
101 |
102 | let errorOptions = ErrorOptions(message: "OMG an error!", tintColor: UIColor.redColor())
103 | viewController.presentError(errorOptions) { () -> () in
104 | viewController.view.presentError()
105 | }
106 |
107 |
108 |
109 | ```
--------------------------------------------------------------------------------
/Swift2-ProtocolExtensions-ErrorRendering.playground/Contents.swift:
--------------------------------------------------------------------------------
1 | //: Playground - noun: a place where people can play
2 | //: Inspired by http://krakendev.io/blog/subclassing-can-suck-and-heres-why
3 |
4 | import UIKit
5 | import XCPlayground
6 |
7 | struct ErrorOptions {
8 | let message: String
9 | let tintColor: UIColor
10 |
11 | init(message: String = "Error!", tintColor: UIColor = UIColor.clearColor()) {
12 | self.message = message
13 | self.tintColor = tintColor
14 | }
15 | }
16 |
17 | typealias ErrorRenderingCompletionBlock = ()->()
18 |
19 | protocol ErrorPopoverRenderer {
20 | func presentError(errorOptions: ErrorOptions)
21 | func presentError(errorOptions: ErrorOptions, completion : ErrorRenderingCompletionBlock?)
22 | }
23 |
24 | extension ErrorPopoverRenderer {
25 | func presentError(errorOptions: ErrorOptions = ErrorOptions()) {
26 | self.presentError(errorOptions, completion: nil)
27 | }
28 | }
29 |
30 |
31 |
32 | extension ErrorPopoverRenderer where Self: UIViewController {
33 |
34 | func presentError(errorOptions: ErrorOptions = ErrorOptions(), completion : ErrorRenderingCompletionBlock?) {
35 | let errorBanner = UILabel()
36 | errorBanner.backgroundColor = errorOptions.tintColor
37 | errorBanner.textAlignment = .Center
38 | errorBanner.adjustsFontSizeToFitWidth = true
39 | errorBanner.font = UIFont.systemFontOfSize(20.0)
40 | errorBanner.textColor = UIColor.whiteColor()
41 | errorBanner.text = errorOptions.message
42 | let height : CGFloat = 50
43 | errorBanner.frame = CGRect(x: 0, y: -height, width: CGRectGetWidth(self.view.bounds), height: height)
44 | self.view.addSubview(errorBanner)
45 | UIView.animateWithDuration(0.8, animations: { () -> Void in
46 | errorBanner.transform = CGAffineTransformMakeTranslation(0, height)
47 | }) { (done1) -> Void in
48 | UIView.animateWithDuration(0.8, delay: 0.5, options: UIViewAnimationOptions.CurveEaseOut, animations: { () -> Void in
49 | errorBanner.transform = CGAffineTransformIdentity
50 | }, completion: { (done2) -> Void in
51 | errorBanner.removeFromSuperview()
52 | if let completionBlock = completion {
53 | completionBlock()
54 | }
55 | })
56 | }
57 | }
58 | }
59 |
60 | extension ErrorPopoverRenderer where Self: UIView {
61 |
62 | func presentError(errorOptions: ErrorOptions = ErrorOptions(), completion : ErrorRenderingCompletionBlock?) {
63 | let errorBanner = UILabel()
64 | errorBanner.backgroundColor = errorOptions.tintColor
65 | errorBanner.textAlignment = .Center
66 | errorBanner.adjustsFontSizeToFitWidth = true
67 | errorBanner.font = UIFont.systemFontOfSize(18.0)
68 | errorBanner.text = "!"
69 | errorBanner.textColor = UIColor.redColor()
70 | let size : CGFloat = 32.0
71 | let padding : CGFloat = 8.0
72 | errorBanner.layer.cornerRadius = size/2.0
73 | errorBanner.layer.borderColor = UIColor.redColor().CGColor
74 | errorBanner.layer.borderWidth = 1.0
75 | errorBanner.frame = CGRect(x: CGRectGetWidth(self.bounds) - size - padding, y: padding, width: size, height: size)
76 | self.addSubview(errorBanner)
77 | if let completionBlock = completion {
78 | completionBlock()
79 | }
80 | }
81 | }
82 |
83 | extension UIViewController : ErrorPopoverRenderer {}
84 | extension UIView : ErrorPopoverRenderer {}
85 |
86 |
87 | let viewController = UIViewController()
88 | viewController.view.frame = CGRect(x: 0, y: 0, width: 375, height: 667)
89 | viewController.view.backgroundColor = UIColor.whiteColor()
90 | XCPlaygroundPage.currentPage.liveView = viewController
91 | let errorOptions = ErrorOptions(message: "OMG an error!", tintColor: UIColor.redColor())
92 | viewController.presentError(errorOptions) { () -> () in
93 | viewController.view.presentError()
94 | }
95 |
96 |
--------------------------------------------------------------------------------
/Swift2-ProtocolExtensions-ErrorRendering.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/Swift2-ProtocolExtensions-ErrorRendering.playground/playground.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Swift2-ProtocolExtensions-ErrorRendering.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/example.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhurray/Swift2-Protocol-Extension-Example/baea4953c1d32513af04438870c0c366279a4474/example.gif
--------------------------------------------------------------------------------
/example.mov:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhurray/Swift2-Protocol-Extension-Example/baea4953c1d32513af04438870c0c366279a4474/example.mov
--------------------------------------------------------------------------------