Skip to content

Instantly share code, notes, and snippets.

@tail-call
Created June 27, 2019 01:50
Show Gist options
  • Save tail-call/40f7fc21c2c96e5ecb7458546810dfca to your computer and use it in GitHub Desktop.
Save tail-call/40f7fc21c2c96e5ecb7458546810dfca to your computer and use it in GitHub Desktop.
Dumped articles
Source: https://medium.com/@hooliooo/yet-another-ios-post-about-creating-views-programmatically-9249a7ab1e93
Date: Thu, 27 Jun 2019 08:49:27 +0700
#[1]publisher [2]Medium
[3]Become a member
[4]Sign in
(BUTTON) Get started
Yet Another iOS Post About Creating Views Programmatically!
[5]Julio Miguel
[6]Julio Miguel
(BUTTON) Follow
[7]Nov 21, 2016 ? 7 min read
I know, I know. There are numerous similar (read: identical) posts
about why creating views programmatically is superior or obsolete
(depending on the author) versus making them via Storyboards/Xibs.
Luckily for you, I won't go over why programmatic views are superior to
Storyboards or Xibs. Why?
Because all three methods accomplish the same thing: creating the user
interface of an iOS application.
There will be endless debates on the topic. Each method has its own
strengths/weaknesses, and everyone has a preference. But more
importantly, each method will be around for as long as iOS is alive.
As my finance professor used to say: "There is more than one way to get
to Nassau Street."
Why I like Programmatic Views
Personally, I learned more about UIView and its instance/class methods
when I switched to creating views programmatically more than I ever did
using Storyboards or Xibs. Storyboards/Xibs didn't really teach me
about the inner workings of UIKit, like when it calls certain UIView
methods. Everything was magic. As a software developer, not knowing how
things work is a HUGE no-no. Personally, it annoys me to no end if I
don't know how things work in software development. I lose sleep over
it.
After scouring the Apple Documentation, Google, StackOverflow, and the
[8]iOSProgramming subreddit, I really improved my understanding of:
1. UIKit/UIView
2. AutoLayout
3. Swift
4. Object Oriented Programming
In addition, you have finer control over what you can do with the user
interface if you create views programmatically. (Who doesn't like
absolute control over what they make?)
Lastly (and also what I think is most important), every single property
of the UIView subclasses you create are all visible in one .swift file.
You don't have to click each UIView in Interface Builder to see what
the properties are set to. Everything you modified is explicit.
Why did I bother making a post about a topic that already has numerous
articles about it?
I've read many articles/tutorials on programmatic views in iOS but none
really adhered to the Model-View-Controller pattern.
The tutorials were brief, and the UIView code was located in the view
controller (you should never be making views in the view controller). I
basically had to guess what to do to separate the UIView code from the
view controller but luckily after searching far and wide on Google, I
found a bit of advice that helped me get started:
A single UIView subclass should be created for each view controller,
essentially acting as a Xib that's written in Swift instead of XML.
The Goal Of This Post
To give the reader (you) an idea of how to write views programmatically
(using SnapKit) without breaking the Model-View-Controller design
pattern and preventing the creation of the Massive View Controller.
This isn't a beginner friendly tutorial
I'm assuming you know the basic project structure of a Single View
Application in Xcode for Swift and know what [9]Cocoapods is and how to
install pods using it. You should have some familiarity with retain
cycles and an understanding of value types vs. reference types.
With that said, let's get started!
The Tutorial! -- Basic Setup
Let's start our journey into programmatic views with everyone's
favorite view: the Login Authentication screen!
But before we get started, let's start with the basic setup.
1. Create a new project: Single View Application
2. Delete Main.storyboard (put in trash and permanently delete)
3. Go to info.plist and delete "Main storyboard file base name"
4. Rename ViewController.swift to LoginVC.swift
5. Rename class ViewController to LoginVC
IFRAME: [10]https://medium.com/media/d8d821f8ecd88c3e16a25e677d33f47d
6. Go to your AppDelegate.swift file, and add the following in the
didFinishLaunchingWithOptions method
IFRAME: [11]https://medium.com/media/f7bff3031a340b23c3d78e9e42ac44ce
Run the simulator. It shouldn't crash, and you should see a green
screen
7. Initialize your Podfile using pod init in the terminal at the
directory of the project you're using to follow along, and install
[12]SnapKit
After installing SnapKit, open up the .xcworkspace file of the project,
and build the project using cmd + b. That should index and compile the
project, and take out any false errors when interacting with Snapkit
pod
8. Create a new .swift file called LoginView, and type the following
boilerplate code. (When you start to override initWithFrame
(Objective-C jargon) initializer this is the boilerplate code the Swift
compiler types for you)
IFRAME: [13]https://medium.com/media/c097683c093296c71161e2c092a18831
Now maybe you maybe thinking at this point, I'll start doing something
like this:
IFRAME: [14]https://medium.com/media/eb1856772d3b3f3bf7a1b370b2e28489
You're half correct. I'm actually going to use closures to set these
subviews up.
IFRAME: [15]https://medium.com/media/120454d23f20d9b67fc763c7f33f4bbb
The setUpKeyboard method is a custom method I have that speeds up
modifying a UITextField's keyboard properties. It is an extension to
UITextField:
IFRAME: [16]https://medium.com/media/3704b7ea453971e7f4b5c9df15e78a63
I also have another helper function called set(cornerRadius:_). All it
does is set the cornerRadius properly on a UIView instance
IFRAME: [17]https://medium.com/media/7144d44332e1a01e132ea588bb58a17d
Now you might be wondering about the closures.
The reason why I use them is because they perfectly encapsulate each
subview's properties away from other subviews' properties. This awesome
separation of logic is what we need to make our code readable.
They are also set once and only once when the subview is instantiated
in LoginView. How do I know this? From [18]this.
Another thing you might be wondering about is the lazy declaration in
the stackView property. The lazy keyword allows you to use self in the
closure. Without it we can't add other subviews in the stackView
closure. Also, the capture list with unowned self is important because
of a possible retain cycle since closures are reference types. The
unowned declaration ensures that doesn't happen.
Also you may be wondering about the various properties I'm modifying in
each subview, and how that might violate the DRY principle. My answer
is each of these properties also exist in Interface Builder, and I
probably am violating DRY considering these two textfields are
identical aside from the placeholder text.
You can create a helper function that takes in a string for the
placeholder text and sets all the other properties to identical values
but my personal preference is to show and explicitly modify each
subview's properties within the closure so a teammate looking at the
code knows what each property is being set to.
You can probably abstract the "AvenirNext-Regular" magic string I
created to a global string enum called Font which has all of your
application's fonts but for the sake of convenience I didn't do that.
Finally, before we can start constraining the subviews with AutoLayout
we have to do one more thing: add the subviews as subviews to
LoginView.
We haven't done that yet! :)
I typically use these helper functions to make this easy for myself:
IFRAME: [19]https://medium.com/media/9f21278fba2c8d2cf69ce53941ed7f7f
So in LoginView's initWithFrame initializer add the following:
IFRAME: [20]https://medium.com/media/d2ae552a582b69583890b4764c5f4948
What about the other subview? They're already subviews of stackView so
by that logic they're in LoginView's view hierarchy already :)
If you don't add stackView as LoginView's subview you'll get an instant
crash.
Also I'm well aware that SnapKit takes care of the
[21]translatesAutoresizingMaskIntoConstraints property for you. But I
think it's good practice to change it yourself.
Now for the constraints!
9. In LoginView's initWithFrame initializer put this code in:
IFRAME: [22]https://medium.com/media/32fd3a91b58f29a74141e9548c142b88
SnapKit makes it easy to create constraints. What constraints did we
put?
1. The stackView is centered in the screen
2. The stackView's leading edge (left edge) is constrained to the
superview (LoginView's left edge) and has an offset of 40.0 points
(which means it's 40.0 points away from LoginView's left edge)
3. The stackView's trailing edge (right edge) is constrained to the
superview (LoginView's right edge) has an inset of 40.0 points
(which means it's 40.0 points away LoginView's right edge)
The emailTextField's height is equal to 7.5% of the superview's height
and because the stackView's distribution property is fillEqually.
passwordTextField and loginButton's heights are also 7.5% of the
superview's height
You may be wondering why we're laying out the constraints code in the
initializer. The answer is because most of the time we need to only do
this once and to make sure it's only done once, do it in the
initializer.
If you have more than one of the same constraint, your application will
build up its memory footprint causing you problems in the future. This
is especially true if it involves a table or collection view full of
cells with duplicate constraints.
If you want to be extra safe use remakeConstraints instead. (It's the
same as makeConstraints except it removes all existing constraints of
the UIView before creating any.
We can't see what the view looks like yet because we haven't connected
it to LoginVC. Let's do that now.
10. In LoginVC, add the following code:
IFRAME: [23]https://medium.com/media/001d0958247948ebaeb62e9085917c26
LoginVC's [24]loadView method is the key to connecting LoginView to
LoginVC.
This method is also one of the few methods in UIKit where you don't
call the super's implementation.
Now we can run simulator. Your simulator should look something like
this:
[1*LSLRhjtCvyCEDB0St26FQQ.png?q=20]
[1*LSLRhjtCvyCEDB0St26FQQ.png?q=20] [1*LSLRhjtCvyCEDB0St26FQQ.png]
11. Making the equivalent of IBOutlets.
So if you used storyboards/xibs you would have had to make code like
this at one point:
@IBOutlet weak var emailTextField: UITextField!
and connected it from the storyboard/xib
Programmatic Views can do something similar and more akin to Object
Oriented Programming :)
Add this to inside LoginVC (The equivalent of putting IBOutlets in the
view controller):
IFRAME: [25]https://medium.com/media/be0c6e987def72b2e40facad8c67dfa3
Oh no! A forced downcast!
Don't worry.
We instantiated LoginVC's view property as an instance of LoginView in
the loadView method therefore, this forced downcast is safe and will
succeed. We also make them weak references to avoid any [26]retain
cycles.
What about IBActions? Can we make those programmatically? The answer:
Yup.
IBActions are just [27]Target Action mechanisms
12. Create a function to be associated with loginButton within LoginVC
IFRAME: [28]https://medium.com/media/b45ca00c4b3468b88049145aa2029e96
So loginButtonPressed will be the action that is invoked when the
loginButton is pressed by the user. How do we connect this function to
the loginButton?
Doing this in viewDidLoad:
IFRAME: [29]https://medium.com/media/437085a9b7fd21a4e86d8fbadfa01249
Congrats! You connected a function to a UIControl programmatically!
Now run it in the simulator and it should work :)
So the final code in LoginVC should be this:
IFRAME: [30]https://medium.com/media/856130fb88a22bdb2d370d5976b0e067
Conclusion!
I hope you found this tutorial enjoyable and informative. I definitely
had fun making it.
Here's the [31]link to the repo for future reference!
If you have any questions please comment away. This was my first
attempt at making an iOS tutorial. Please let me know on what I can
improve on.
(BUTTON) 462
* [32]Swift
* [33]iOS
* [34]Programming
* [35]Mobile App Development
(BUTTON) 462 claps
(BUTTON)
(BUTTON)
[36]Julio Miguel
Written by
[37]Julio Miguel
(BUTTON) Follow
Finance analyst turned iOS Developer. Trying to learn anything and everything
about software development in this lifetime.
(BUTTON) Follow
See responses (6)
References
Visible links:
1. https://plus.google.com/103654360130207659246
2. https://medium.com/osd.xml
3. https://medium.com/membership?source=upgrade_membership---nav_full------------------------
4. https://medium.com/m/signin?operation=login&redirect=https%3A%2F%2Fmedium.com%2F%40hooliooo%2Fyet-another-ios-post-about-creating-views-programmatically-9249a7ab1e93&source=--------------------------nav_reg-
5. https://medium.com/@hooliooo
6. https://medium.com/@hooliooo
7. https://medium.com/@hooliooo/yet-another-ios-post-about-creating-views-programmatically-9249a7ab1e93
8. http://reddit.com/r/iOSProgramming/
9. https://cocoapods.org/
10. https://medium.com/media/d8d821f8ecd88c3e16a25e677d33f47d
11. https://medium.com/media/f7bff3031a340b23c3d78e9e42ac44ce
12. https://github.com/SnapKit/SnapKit
13. https://medium.com/media/c097683c093296c71161e2c092a18831
14. https://medium.com/media/eb1856772d3b3f3bf7a1b370b2e28489
15. https://medium.com/media/120454d23f20d9b67fc763c7f33f4bbb
16. https://medium.com/media/3704b7ea453971e7f4b5c9df15e78a63
17. https://medium.com/media/7144d44332e1a01e132ea588bb58a17d
18. http://stackoverflow.com/questions/31515805/difference-between-computed-property-and-property-set-with-closure
19. https://medium.com/media/9f21278fba2c8d2cf69ce53941ed7f7f
20. https://medium.com/media/d2ae552a582b69583890b4764c5f4948
21. https://developer.apple.com/reference/uikit/uiview/1622572-translatesautoresizingmaskintoco
22. https://medium.com/media/32fd3a91b58f29a74141e9548c142b88
23. https://medium.com/media/001d0958247948ebaeb62e9085917c26
24. https://developer.apple.com/reference/uikit/uiviewcontroller/1621454-loadview
25. https://medium.com/media/be0c6e987def72b2e40facad8c67dfa3
26. https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html
27. https://developer.apple.com/library/content/documentation/General/Conceptual/CocoaEncyclopedia/Target-Action/Target-Action.html
28. https://medium.com/media/b45ca00c4b3468b88049145aa2029e96
29. https://medium.com/media/437085a9b7fd21a4e86d8fbadfa01249
30. https://medium.com/media/856130fb88a22bdb2d370d5976b0e067
31. https://github.com/hooliooo/swift-programmatic-views
32. https://medium.com/tag/swift
33. https://medium.com/tag/ios
34. https://medium.com/tag/programming
35. https://medium.com/tag/mobile-app-development
36. https://medium.com/@hooliooo?source=follow_footer--------------------------follow_footer-
37. https://medium.com/@hooliooo?source=follow_footer--------------------------follow_footer-
Hidden links:
39. https://medium.com/
40. https://medium.com/m/signin?operation=register&redirect=https%3A%2F%2Fmedium.com%2F%40hooliooo%2Fyet-another-ios-post-about-creating-views-programmatically-9249a7ab1e93&source=post_sidebar-----9249a7ab1e93---------------------clap_sidebar-
41. https://medium.com/m/signin?operation=register&redirect=https%3A%2F%2Fmedium.com%2F%40hooliooo%2Fyet-another-ios-post-about-creating-views-programmatically-9249a7ab1e93&source=post_sidebar--------------------------bookmark_sidebar-
42. https://medium.com/m/signin?operation=register&redirect=https%3A%2F%2Fmedium.com%2F%40hooliooo%2Fyet-another-ios-post-about-creating-views-programmatically-9249a7ab1e93&source=post_actions_footer-----9249a7ab1e93---------------------clap_footer-
43. https://medium.com/p/9249a7ab1e93/share/twitter?source=follow_footer--------------------------follow_footer-
44. https://medium.com/p/9249a7ab1e93/share/facebook?source=follow_footer--------------------------follow_footer-
45. https://medium.com/m/signin?operation=register&redirect=https%3A%2F%2Fmedium.com%2F%40hooliooo%2Fyet-another-ios-post-about-creating-views-programmatically-9249a7ab1e93&source=post_actions_footer--------------------------bookmark_sidebar-
46. https://medium.com/p/9249a7ab1e93/responses/show?source=follow_footer--------------------------follow_footer-
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment