studying the video by Sean Allen
Learning Swift — Day 221(235)
Download the starter project linked in the description and create a new project.
Go to ViewController.swift, declare and instantiate a new UIImageView()
then, in viewDidLoad()
call the yet unwritten setBackground()
method and start creating it below.
Inside it call view.addSubview()
passing our image view as argument.
- Then call the TAMIC (
translatesAutoresizingMaskIntoConstraints
) property on the image view and set it tofalse
. - Now pin the image view to top anchor of the super view (
backgroundImageView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
). - Repeat the process for all four sides.
- Tell Xcode which image to load:
backgroundImageView.image = UIImage(named: "background-wave")
. - Send the image view to the background:
view.sendSubviewToBack(backgroundImageView)
.
Switch to Main.storyboard.
- Drag a Button on the Storyboard and, in the Identity Inspector, change its class to be
SAButton
. - Click on Add New Constraints in the bottom right and pin it 30 points to the right and to the left of the View and 20 points from the bottom. Also, give it a height of 50.
- Drag a Text Field above the button and change its class to be
SATextField
. - Give it a leading and trailing constraints of 30 points from the view and a bottom constraint of 40 points from the top of the button, then give it a height of 50.
Now:
- Drag an ImageView to the canvas and make it 250 high and wide, before centring it in the middle of the container.
- Set the image of the image view inside of the Attributes Inspector to “wave-logo”.
- In the Size Inspector, change the image view size to 190 x 190 and change the constant of the Y alignment to -125.
Next:
- Add a Label with “WaveFinder” written inside using the custom font AvenirNext Condensed in DemiBolt weight and 22pts height.
- Pin it 20 points to the edges, then pin its “vertical space” from the logo.
- Set the font colour to white and the autoshrink option to minimum font size = 20.
That’s it!
On to the next topic!
Save Data with UserDefaults
Studying the video by Sean Allen
This will be a review for me, but a most welcome one!
Download and open the project linked into the video. Unfortunately the downloadable project was only the completed one so I will simply follow on the video without typing anything.
How to use the Coordinator pattern in iOS
Studying the video by Paul Hudson.
This is going to be very very interesting!
The issue
UIViewController… is it a view or a controller?
Apple implementation is cumbersome because of how much functionality it contains, risking to become a massive view controller.
In a UIViewController
we have: user interface design, code to update views with model data, code to respond to user input, to save and store state, we find data sources and delegates, animation code, networking and navigation.
Navigation is there to control the flow of our app, to handle iPad/iPhone variants or to handle A/B testing variants (which means?).
Solution: use the Coordinator pattern
Introduced by Soroush Kanlou, starts with this line of code:
navigationController?.pushViewController(someVC, animated: true)
ViewController A has to know, configure and display ViewController B. In the Coordinator pattern the connection is broken. ViewController A speaks to a Coordinator which then takes action configuring and popping views. In bigger apps this Coordinator is a protocol and it can even be split into several parts with “master coordinators” and “children coordinators”.
How to implement
This takes three steps
- Create a coordinator protocol:
protocol Coordinator {
var children: [Coordinator] { get set }
var nav: UINavigationController { get set }
func start()
}
- Create a concrete implementation of that protocol:
class MainCoordinator: Coordinator {
func start() {
let vc = ViewController()
vc.coordinator = self
nav.pushViewController(vc, animated: false)
}
}
- In the view controller ask the coordinator to perform any action.
This brings us the S of the SOLID pattern (?), which is the Single Responsibility Principle (just following, right?!).
Benefits
- No more hard-coded flow.
- True isolation between VCs.
- It’s all code we control.
Code Demo
Create a new Single View App project, call it “Coordinators” and save it somewhere sensible. Inside, create a new Swift file called “Coordinator” and perform step 1 from above after importing UIKit.
Create a new Swift file called “Storyboarded”, import UIKit, declare a protocol Storyboarded
with a single static func
inside called instantiate()
which returns Self
, that is an instance of the type that conforms to the protocol.
In an extension to this protocol, provide a default implementation of the instantiate()
function: instantiate an id
constant equal to a String describing self
, then a storyboard
constant equal to a UIStoryboard
with name “Main” and belonging to the “Bundle.main” bundle. Finally return storyboard.instantiateViewController(withIdentifier: id)
, forcedly downcast to Self
. This allows us to create view controllers directly from the storyboard using their class name!
In ViewController.swift add conformance to the Storyboarded
protocol.
In the storyboard, select the primary view controller and, in the Identity Inspector, give it a Storyboard ID of “ViewController”.
Create a new Swift file called “MainCoordinator”. Import UIKit then instantiate a variable called childCoordinators
which is an array of Coordinator
s objects. Then add the navigationController
property and write an initialiser like this:
init(navigationController: UINavigationController) {
self.navigationController = navigationController
}
Finally implement the start()
method creating a vc
constant in the form of ViewController.instantiate()
(which gets this method from conforming to the Storyboarded
protocol and then call navigationController.pushViewController
passing vc
and false
as arguments.
In the Project Settings delete “Main” from the Deployment Info > Main Interface line.
In AppDelegate.swift add an optional MainCoordinator
variable property called coordinator
. Inside the application(_:didFinishLaunchingWithOptions)
method, instantiate a new UINavigationController
, set the coordinator
property to MainCoordinator(navigationController: navController)
and call the start()
method on the coordinator. Then, set the window to UIWindow(frame: UIScreen.main.bounds)
, set its root view controller to the navigation controller declared just previously, then make it key and visible.
Now, this doesn’t work because of the introduction of the SceneDelegate.swift file. Now I have no idea where I should put this…
Fantastic … now I am stuck again and I will not be able to finish this tutorial … FANTASTIC! Damn it!
Let’s move on by now, until someone will help me understand how this s*** should work …
Create two CocoaTouchClasses, CreateAccountViewController
and BuyViewController
, both conforming to UIViewController
and Storyboarded
.
Unfortunately, from this point onwards, things stop working in the iOS 13 newly created version of this pattern. I am now writing out to people who should know what is happening but it is pointless for me to continue banging my head against something that is not explained, nor clarified. As I said, I refuse to try things out myself if there is no point in it. I tried moving the code between the various methods of AppDelegate and SceneDelegate, to no real avail.
So I’m sorry but I have to interrupt this article here. I will — probably — update it if and when someone helps me understand what should be done here.
Thanks for reading.
If you like what I’m doing here please consider liking this article and sharing it with some of your peers. If you are feeling like being really awesome, please consider making a small donation to support my studies and my writing (please appreciate that I am not using advertisement on my articles).
If you are interested in my music engraving and my publications don’t forget visit my Facebook page and the pages where I publish my scores (Gumroad, SheetMusicPlus, ScoreExchange and on Apple Books).
You can also support me by buying Paul Hudson’s books from this Affiliate Link.
Anyways, thank you so much for reading!
Till the next one!