Now, in the past few lessons, we've already massively improved some of our user interface, and it's starting 1
to really come along and look really nice. 2
So in this lesson, we're going to tie up all the loose ends and we're gonna do a little bit more 3
front-end stuff to make our app feel really cohesive and look like it's a real app on the App Store. 4
So the first thing that I want to do is I want to change the nav bar. Instead of having this small nav 5
bar, 6
I want to make it a large title. And in order to do that, there's only one checkbox that we need to check 7
off. 8
So if you go into your Main.storyboard and select, not the nav bars for your individual view controllers, 9
but the actual navigation controller's nav bar. And you can also select it inside the document outline 10
as well, 11
it's this particular one that you're interested, 12
the actual bar rather than the controller. If you head over to the attributes Inspector, 13
then there's this particular Boolean called Prefers Large Titles. And if you tick it, then you can see that 14
our navigation bars become a lot bigger 15
and the titles get bigger as well. 16
And it looks a lot more like an actual todoList. 17
And if we run our app, you'll see that it looks a lot nicer 18
in this particular format. It works much better as the heading. 19
So the next thing I want to change is that I want to change the nav bar to be consistent, also with the 20
background color of the category, so that everything on this screen is green. And the place where you're 21
going to set this is inside viewDidLoad. 22
So inside the TodoListViewController, go into the viewDidLoad and we are going to tap into our 23
navigationController.navigationBar. And then we're going to set, not it's background color, because this only 24
changes the background color of the bar, 25
but we're going to set the barTintColor. And this will apply it both to the navigation bar as well 26
as the status bar. And we're going to set this equal to the color based on the selected category. 27
So it'll be selectedCategory.colour. And, of course, remember, this is a hex string that describes a 28
color. 29
So we're going to have to use that UIColor method from Chameleon called UIcolor with hexString, 30
and then we're going to put that hexString inside here. 31
Now, as soon as you do that, Xcode is going to complain because we're using optional chaining here. 32
This value is an optional. And this UIColor using hexString method needs a nonoptional string in order 33
for this to work. 34
So you have the option of either explicitly unwrapping selectedCategory, which is not all that safe, 35
or you can use optional binding. 36
So we can say if let colourHex = selectedCategory?.colour. If this is not nil, then 37
we will proceed with this method and we can replace this part here with colourHex. 38
Now, the other assumption that we're making here, by using this question mark is we're saying if the 39
navigationController is not nil, then in that case, we're going to set the bar color for the navigation bar. 40
Now, this assumption is a little bit problematic ,and I'm going to show you why by using a "guard" statement. 41
So I'm going to say guard let navBar = navigationController.navigationBar, and then else, i.e., if it 42
is nil, then we're going to throw a fatalError 43
and I'm going to pass the message for that fatalError as "Navigation controller does not exist." 44
So what this line is going to do is it's going to create this constant called navBar and assign that 45
the value of the current navigationBar in the current navigationController. 46
But if this is nil, i.e., then navigationController does not yet exist, then we're going to throw this 47
fatalError and we're going to inform ourselves of this problem. And our app is going to crash and we're 48
going to see this message. 49
So let's run our app and see what happens. 50
Okay. 51
So let's go into our 52
TodoListViewController. And you can see that our app is right now crashing. And if we click on this error, then it says, 53
"Fatal error: Navigation controller does not exist." 54
So the thing is when you start off learning how to develop apps for iOS, people tend to be very afraid 55
of app crashes. 56
But as you get more and more experience, you should start to learn that crashes are not bad. They're not 57
a bad thing. 58
What you don't want is for your app to fail silently in the background, and your variables and property 59
is having values that are unexpected and undesired. 60
But it's not giving you a warning in any sort of way. 61
So don't be afraid of throwing fatal errors when you have a situation where you definitely don't expect 62
it to break so that you can catch these problems during testing, rather than in the live deployment app 63
on the App Store when your app is not behaving the way it should be 64
and the user has no clue how to report this. 65
So, now let's stop our app and figure out what's going on. 66
Why is our navigationController nil at this point? 67
Why are we getting this fatal error being thrown? 68
Well, the problem is that we know that viewDidLoad is the time point at which our view for our current 69
view controller has been loaded up. 70
Now, the problem is that just because the view has been loaded up, it does not mean that your view has been 71
inserted into the navigation controller, 72
it might not yet be in that navigation stack. 73
So during the process of pushing this TodoListViewController onto the navigation stack to be managed 74
by the navigation controller, viewDidLoad will get cold. 75
But the thing that you cannot be sure of is whether if the TodoListViewController is already inside 76
the navigation stack, i.e., if that process has completed before viewDidLoad gets called. 77
So in our case, it's clear that viewDidLoad is actually being called before the navigationController 78
property has been updated, because at this point, it is still nil. 79
So what can we do? 80
Well, maybe consider that viewDidLoad might not be the right place to place this code. And if we think 81
back to the lesson on view controller life cycles, then you might remember that there's another access 82
point that we can use which was called viewWillAppear. And viewWillAppear gets called later than 83
viewDidLoad and it's actually at the point just right before the view is going to show up on the screen. 84
So after the view has been loaded up after the navigation stack has been established, and just before the 85
user sees anything on screen, this is the time point that we can access using viewWillAppear. 86
And so I'm going to transplant my code here into viewWillAppear, and it's probably also a good time 87
to delete this stray bit of code here if you haven't done already 88
because we no longer need that. 89
Okay. 90
So inside our viewWillAppear, we're still going to use our guard let navigationBar. And Xcode is complaining 91
and giving as a warning at the moment that you've guarded and you've created this constant called 92
navBar. We're not really using it anyway. 93
So let's use it. Let's replace this navigationBar that's uses optional chaining. 94
So this is why our barTintColor didn't change at all previously because navigationController is nil 95
and the way that optional chaining works is that if this is nil, then this code is not going to be continued. 96
So this gets skipped over. 97
So instead, what we want to do is we want to use navBar and we're guarding against scenarios where navBar 98
is nil by using this guard statement 99
So let's go ahead and run our app and see if that worked. 100
Okay. 101
So we're going to go into the Home screen and you can see that our status bar, our navigation bar, everything 102
gets changed just by moving our code to a different life cycle time point. And our app doesn't crash, 103
our fatal error is not being thrown. Because at this point, the navigation property has a value, it's not 104
nil. And our current view controller is firmly inside the navigation stack. 105
So the other things that we might want to do here is maybe change the title of our 106
TodoItemsViewController. 107
It would be nice if this title reflected the category that we selected, 108
right? 109
And it's pretty easy to tap in to because we have this selectedCategory property here. 110
So we can simply say something like title, which is a string that represents the view that this controller 111
manages, 112
and this is basically what the navigationController will tap into to populate the title in the 113
navBar. 114
So we can say title = selectedCategory. 115
And because we're inside an optional binding block where we've already access the selectedCategory's 116
color through optional binding, then we can be pretty sure that selectedCategory does in fact exist 117
and it's not nil. 118
So title will become selected category.name. 119
So as you progress, you're going to be more flexible with these optionals and figure out ways of being 120
safe while being constantly aware of what it is that your code is doing, and the places where it can 121
fall down. 122
So let's run our app and see if our title gets populated. And if we go inside Home, you can see that our 123
title used to be Item, but it now is the title of the Category. 124
So looking pretty sweet, right? Now, there's just one other thing that's bugging me which is, what about 125
the background of the search bar, 126
it looks so out of place with that grey background. 127
Let's change it. 128
So for us to be able to do that, we first have to create an outlet from the search bar into our 129
TodoListViewController. So you can either click and drag from the search bar just making sure that you've 130
actually selected the search bar and not a table view. 131
Alternatively and probably a much better way is to go from the document outline and drag it into your 132
TodoListViewController. 133
And I'm just going to call it searchBar and hit enter to create that IBOutlet, and then we can go inside 134
viewWillAppear just underneath the line that we wrote previously. 135
So, we'll tap into searchBar.tint Color 136
and this applies the tint color to the search bar background 137
and we're going to set it to equal the same color that we've specified for our navigationBar. 138
So let's go ahead and hit run and see if that worked. 139
So we're going to go into Home and you can see everything is now more or less green. 140
Now, another thing that you might want to change is the color of these buttons. 141
So, for example, this button currently is sort of a light blue which doesn't look that great. 142
We manually set this button to be white. 143
But what if our navigation bar was set to a really light color, for example, white or something like that, 144
then in that case we might not be able to see these buttons, 145
right? 146
So we should do what we've done here which is set these buttons to contrast the color of the navigation 147
bar and we can do that just below where we set the barTintColor because there's another property in 148
the navigation bar called the tintColor. 149
And this applies to all of the navigation items and bar button items. 150
So that is the one that we want to change. 151
We're going to set it to the contrasting color of the color of the navBar and we're going to return 152
it as flat which basically means a low contrast color. 153
Now, we have a problem here because the output of this particular method is a optional UIColor and that's 154
fine over here because barTintColor takes a optional UIColor. 155
But in this case, the contrasting color method requires a nonoptional UIColor. 156
So we either have to force unwrap it which is unsafe or we will have to create another if let. 157
So if let fnavBarColour = UIColor created with the colourHex code. 158
And if that is not nil, then we will set the navBar background the tintColor as well as the search 159
bar colors. 160
So we're going to paste that inside this optional binding statement, and then we can change this to 161
navBarColour, and change this to navBarColour, and also we can change this to navBarColour because it's gonna 162
be the same. 163
Now, the next thing that we want to change is the color of the title. 164
And if we have a look through the UINavigationBar Documentation, then you can see that there's a section 165
called Customizing the Appearance of a Navigation Bar, 166
and it talks about how you would change the title text differently from the rest of the controls, so 167
the bar button items, for example. And you need to change this titleTextAttributes property by setting 168
the foregroundColor key. 169
So let's go ahead and do that here. 170
Now, we're gonna say navigationBar to tap into the current bar. 171
Then we're going to look for the titleTextAttributes. 172
Now, it's important that because we're using the large title, we have to change the properties for the 173
largeTitleTextAttributes, rather than just the regular titleTextAttributes which is the smaller 174
one that shows up in the middle. 175
So let's select largeTitleTextAttribute property, and it's expecting a dictionary where it takes a 176
key which is an NSAttributedStringKey 177
and it takes a value which is going to be the value for whichever key you choose. 178
So we're going to create a new dictionary and we're going to tap into that 179
NSAttributedStringKey. And we're going to use the dot notation to find the key that we want. 180
And this is a bit like an enum where we're looking through and trying to find the value that we want 181
to change. 182
And in our case, it's actually the foreground color that we're interested in changing, the foregroundColor 183
for the large text title. 184
And this is going to be set to our contrast color contrasting the navBarColor returning a flat color. 185
And now, we're almost ready to run our app. 186
The last thing that we need to do before we can do that is we need to go into our Main.storyboard and 187
click on that plus button which we manually set to be white 188
and we need to change it back to default. 189
And now if we run our app and we go into Home, you can see that everything gets colored correctly, and 190
also the background color looks exactly as we want it to.. Now, there's just one problem, the navigation 191
bar and the category view controller is not behaving as expected. In order to fix this, 192
let's change the navigation bar's background color to blue in the CategoryViewController. 193
I'm gonna do this in the viewWilllAppear lifecycle method. 194
So the part where it says override function viewWillApear or add the same guard let as in the 195
TodoListViewController. 196
So guard let navBar equals navigationController.navigationBar 197
else through a fatalError that "Navigation controller does not exist." Except for this time, I'll change 198
the background color to the UIColor with the hex code pound sign 1D9BF6. So navBar. backgroundColour 199
= UIColor with hexString pound sign "1D9BF6." And if we run our app, you'll see that it works 200
exactly as it did before. 201
Now, the very, very, very last thing that I want to fix is the text color inside the cell for the 202
CategoryViewController here, because I can barely read it off that dark background. 203
So we know how to do that. Inside our TodoListViewController, 204
when we did cellForRowAt indexPath, we had this line of code where we contrasted the color of the background 205
color. 206
So I'm gonna go into the CategoryViewController and go into its selfForRowAt indexPath and I'm going 207
to set the cell's textLabel.textColor to be the contrasting color of this color which came from 208
the category and returning as a flat color. 209
Now, of course, if you wanted to, you can either use if let or guard let categoryColour to be equal to this 210
UIColor created using that category. colour hexString. And if that doesn't work, then I'm gonna 211
throw a fatalError because it really should. 212
Now, I can set the cell's backgroundColor to categoryColour, and I can contrast the color, instead of this 213
long string to categoryColour as well. 214
And let's run our up and see if that worked. 215
All right. 216
Nice. 217
That's looking a lot better than before. 218
As you can see, there's so much that you can do with the front-end and the UI. 219
You can really go to town if you wanted to. 220
And off the top of my head, I can probably think of even more UI improvements, but we have achieved most 221
of our design aims. 222
We wanted to have different colors for each category and that we wanted that color to be carried over when 223
we go into the TodoListViewController. 224
We have the category as the title of this TodoListViewController and we've created this gradient 225
effect for each of our cells, and we've contrasted the text with that background. 226
So if you do decide to add even more front-end code and make this even more fancy and even prettier, 227
then be sure to post a video of it or a GIF of it to the Q-and-A section so that we can all admire your beautiful 228
work. 229
So I hope you enjoyed making this app with me and learning all about, oh, my gosh, so many things in this 230
module. 231
The main reason why I created this module was to teach you all of the different ways that you can persist 232
this data locally. 233
And we covered user default, 234
we covered the quotable protocol. 235
We covered file storage locally as well as Core Data and Realm which we've used to create relational 236
databases. 237
And finally, we've wrapped it all up using a nice UI, and we now have this beautiful to-do list app which 238
you will no doubtedly use to add new to-do items like other new iOS codes that you're going to learn 239
about. 240
That's all for me for this module and I will see you on the next module.