Lecture 1: Course Logistics and Introduction to SwiftUI

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
(regal music) - [Announcer] Stanford University. - Welcome to Stanford CS193p. This is spring quarter of 2020. I'm Paul Hegarty, and I'm going to be your tour guide for this adventure into developing applications for iOS using SwiftUI. And it's gonna be quite an adventure this quarter because none of you are here on campus with me which is kind of sad for me. It's gonna be a little bit strange. I'm sure it's gonna be very strange for you all. And it's not really gonna affect us that much in this course, believe it or not, because most of the communication that happens between you and me is happening via our class forums, Piazza. So you definitely wanna get on Piazza and ask questions there. Don't be shy about that. We get very quick responses here. See what your fellow students are asking also. And definitely be looking for posts from me because all course materials, all announcements are gonna be made through Piazza. So don't miss that part of it. Now, all the lectures are gonna be pre-recorded just like this one. And that's gonna be convenient for you I guess because after they come out, you can watch them at your leisure and fast forward and rewind. And especially since I'm going to be doing a lot of demos, that is gonna be really valuable. I guess it's also gonna have a great benefit which is that as we do many times in this course, we release the course out into the world for free. So people can watch this. Spy on us basically here as we're doing this course at Stanford. And we'll be doing that again after we do the standard closed captioning and other post-production work. So if you're watching me outside Stanford, welcome to you as well. Now, of course you're not gonna be able to participate in the forums with the rest of the Stanford students because you're watching this a little bit later. But there are community-based resources for asking questions out there in the world like Stack Overflow. So definitely go and check those out as well. This SwiftUI thing is brand new, literally just a few months old. So you are on the cutting edge of technology when it comes to developing applications for iOS. And as you learn this, some things are gonna be familiar to you. There's a little bit of object-oriented programming in there. This language that you're gonna learn is kind of C-like. It shares some syntax with languages like Java as well. So that'll be familiar. But you're probably gonna be learning some pretty much brand new stuff for you as well. For example, you're gonna be learning a new programming language, Swift. Entirely new for almost all of you, I'm sure. And this programming language, while it supports object-oriented programming, it also supports a different kind of programming called functional programming or protocol-oriented programming. And that I'm not gonna assume you know. So I'm gonna be teaching you all about that along the way. The user interface paradigm is also what's called reactive. And it's declarative rather than imperative. And I'm gonna explain what all that means so that's gonna be new to you. And there might be some other miscellaneous new topics. For example, I'm hoping to get to cover iOS's object-oriented database which is really cool technology. But in any case, all that you're gonna learn in this course is really just kind of a survey course or a collection of real-life application of stuff you're learning in your other CS classes here. For example computer-human interfaces, obviously. API design and language design. Animation, persistence, networking, multithreading, all these things listed here are absolutely going to be things that we include in this course. And so you're gonna see it all together in a real-world environment. So let's talk a little bit about the mechanics of the course like how it's actually gonna play out. And in these pre-recorded lectures, I'm gonna try and spend most of my time doing demos. I really believe in demos. I think they make the concepts very concrete. So I'll probably spent around 2/3 of the time on demos. But occasionally I do have to go to the lecture slides. And this is to cover concepts that if you didn't understand them, the demos might just not make any sense. So for example, at the beginning of lecture two, I'm gonna be covering MVVM, the design paradigm we use to organize our code. I'm also gonna be covering the type system in Swift 'cause that's just so critical to understanding how Swift works, to understand its type system. But again, if I can, I always lean towards the demo because we wanna spend our time actually doing iOS development. That's what we're here to learn. For your part, you are going to have some reading to do the first three weeks. This is mostly to learn the language, Swift. There's an online Swift programming reference manual that I'm going to guide you through those first three weeks to try and get you efficiently through it. Because it's a rather large document and I don't want you to be spending an inordinate amount of time there. But by the same token, you do have to know Swift to be able to do SwiftUI. There will be programming assignments of course. They are usually about a week long. There's four or five of them. This quarter's a little bit short, starting a week late. So these four or five programming assignments will be in the first six weeks. And the reason for that is I leave the last three weeks for you to work on a final project. And this final project is the culmination of everything you've learned, so we won't have any programming assignments during that final three weeks. If you go to the online forums, the class forums, there's a lot more information there as well. And the syllabus is posted there so I encourage you to read that if you haven't already. Hopefully, you have. Let's get started here building our first app. I have a little video that I've made of what this first app that we're gonna build looks like. So this is what it looks like. It's a card matching game. Those of you who have seen this course on video in the past will recognize it. But of course this one's all written in SwiftUI so it's a completely new app. And it's got a lot more features than the previous ones. Some things are really easy to do in SwiftUI. So you can see this has a lot of animation. These cards are flipping over. Numbers are flying up and down as you match and mismatch cards. You've got that little colored ring that's kind of spinning down in the background. So we're gonna be doing a lot of animation in this course. Animation is very important to a good mobile UI. You can also see that this application's got multiple themes, different colors. We had that Halloween version. Here we have these flags. Our cards are just showing emojis. Obviously my little video there is going very quickly clicking around, but you are watching it on video so you can just back up and pause and take a close look at some of the animations and the things that are going on there. So that's what we're gonna spend the first really two or three weeks in demo and in your homework, working on building this application. By the way, you're probably not gonna see me on camera much the rest of the quarter 'cause we're gonna be doing screen records. So you'll just hear my disembodied voice. By the way, my disembodied voice will sound a little different because I'm gonna use my AirPods when I'm doing the screen recordings. It picks up less of the mouse clicking and the keyboard tapping and all that. So if you don't see me again the rest of the quarter, just enjoy my disembodied voice. So all the work that we do is going to be in an application called Xcode. It's the development environment for iOS and it's all inclusive. It includes the source-code editing compiler, debugger, it's all in one place. And you can get it for free from the App Store if you just go on your Mac to the App Store and search for Xcode. You're gonna find it's probably the very first match. you can see it right here. And when you launch Xcode, it's gonna look like this. Now, your first required task of your very first assignment is to reproduce everything that I'm doing in the first two lectures. So you might wanna pause this video and go download Xcode and be ready to follow along with me. Or you can just watch this video at your leisure, kind of soak it all in and then go back and rewind, fast forward through it to satisfy that first required task. So here this splash screen, it has two sides to it. This is Recent Projects which will build up as the quarter goes along. And then over here is essentially how we get into a project. And we're gonna choose this second line right here to create a new Xcode project. Now, Xcode can be used as we mentioned earlier, not just for iOS apps but also Watch apps and Apple TV apps, even just Mac apps. We're gonna focus on building iOS apps in this course, but everything that you're learning about SwiftUI, it's applicable to all these other platforms as well. Now, these icons here are all just templates. Essentially, prepackaged little bits of code to get you started with apps of certain types. But we're gonna be choosing Single View App I think for every app this quarter. And it's the simplest of the templates and you're gonna see it really doesn't generate a lot of code to get you started. And so let's just double-click on that. Now, it wants to know some interesting things about your app, most notably the name. So I'm gonna call it Memorize. For those of you who might be watching this who have seen previous videos of the quarters in the past of this course, you'll know that I did this memory matching game before. Not in SwiftUI obviously, because that's brand new. And I called it Concentration instead of Memorize. And I did that for a reason here, used a different name so that if we wanna refer to it, I can mention it. And when I say Concentration, I mean the old non-SwiftUI version. And doing the same app is gonna allow those of you who maybe already know some UIKit or have some experience doing that to compare and contrast what it looks like to do it in SwiftUI. So this second thing right here, team, that's your development team. In this case it's a personal team, just me. And when you launch Xcode, this is probably gonna say something like Add Team. And when you click on it, all you'll need is any Apple ID and you can create your own team. If you worked for a company, it could possibly be a company development team here. The organization name, this is gonna be your name. This is going to appear at the top of files, written by CS193p instructor. So you wanna put your name there. This wants to be a very unique identifier of you. So if you're a Stanford student, I recommend using this reverse DNS notation. edu.stanford.yoursunetid is probably good enough to identify you. You could put the CS193p in there if you want. If you're a Stanford student, it probably doesn't matter. If you're not a Stanford student and you're watching this, maybe your email address reversed or your company's address reversed. You just wanna make sure it's unique. You wouldn't wanna pick something that someone else might also be choosing. You could see that Xcode makes this unique identifier for your app out of these two things. And then we're gonna choose the language. The underpinnings of what is in iOS, it was all written in this language, Objective-C, which is this object-oriented version of C. But about five or six years ago, Apple came out with this new language, Swift, which is awesome because it's completely compatible with Objective-C. So all their existing libraries all just worked. But it introduced a lot of modern language features, especially support for functional programming as opposed to object-oriented programming. Swift does both and you're really gonna see that in SwiftUI because SwiftUI is based on functional programming, not object-oriented programming. So I don't have time to teach you two languages, I barely have time to teach you one new one. So we are only going to be programming in Swift. And of course SwiftUI is all using Swift, based on Swift. This other user interface choice is if you wanna develop in the old way, the non-SwiftUI way. I'll often call this UIKit instead of SwiftUI. We're gonna do all our development in SwiftUI, although the last week or two of the quarter, hopefully I'll have time to show you how to integrate the old stuff into SwiftUI. Because SwiftUI doesn't cover all the ground that the old UIKit does. So it's nice to be able to kind of glue it in there. And so there's some good glue in SwiftUI to glue the UIKit stuff in. These other switches in the bottom, we might get time by the end of the quarter to do this Core Data. It's an object-oriented database. I'm hoping to have time to do that. Unfortunately, I'm probably not gonna have time to do this testing framework. Which really I don't want that to make it sound like testing is not important. It's super important. It's just that you can't really test something until you know how to develop for it and we're just gonna run out of time before I can go back and cover that. So for our first assignment, we're not gonna be doing any of these so you can leave all of that unchecked. So I hit Next here and now it wants to know, "Okay, great, you wanna create this app. "Where do you wanna put it?" And I strongly recommend putting it in a folder called Developer in your home directory. That is kind of the canonical place that we put things. And I recommend strongly that you do that. Down here at the bottom you can see this Source Control thing. Source control for those of you who are not familiar with it, it's a way for you to manage the changes to your code, kind of check them in and check them out. It's especially valuable when you have a team because a team's working on the same code, their changes might conflict or whatever. So you wanna have a mechanism for arbitrating those changes. And so you can have this checked or not. I'm not gonna have it checked here. Later in the quarter I'll try to go through some of the features of the source control integration in Xcode 'cause it's pretty cool. But for now, either way is fine. So this is gonna create our first app. Now, many of you are not familiar with this whole user interface here, this Xcode user interface. So I'm gonna go through it briefly and I'm gonna start in the upper-left hand corner right here, this little row of buttons. This is essentially how we're going to run our application. Now, if you click on this button here, you can see that you can run your application on a device if you have it actually connected to your Mac which I don't currently have. Or you can run it in a simulator. And these simulators are pretty amazing. They're a pretty full simulation that I'm gonna show you in a second here. And I'm in fact gonna pick iPhone 11 as my simulator. And I can just run this SwiftUI app that this template created for me by clicking Play. So when I click the Play button, it's gonna launch the simulator. Now, the first time you do this, it's gonna take a little bit of time because the simulator fully simulates the device. And in fact I'll show you that. So here's our app, it says, "Hello, World!" You can probably can imagine from this code over here. And that's what all first apps do, they say "Hello, World." But if we swipe up from the bottom just like on a real device, you can see that there is the App Chooser here and I can go into Settings. Here's the Settings app and I could set settings about my device, et cetera. It's not 100% a full device, but it's the parts of the device you would need to make sure your app is really working. I don't know if you also notice that this device is similar, it happens to be in Dark Mode which is new in iOS 13. And I'm gonna leave it in Dark Mode because we're gonna wanna look at our app in both Dark Mode and non-Dark Mode. And SwiftUI handles most of that for us. So I'm just gonna go back to the App Switcher here and switch back to our app. Here it is, it says, "Hello, World!" Let's go back and see how we got to this. How we get to this whole Hello, World business. So I'm gonna press this square right here. This is Stop, that stops the simulator. And let's continue looking at Xcode's UI. On the left here, this little blue area which we can resize to what we want. It's called the Navigator. And it lets you navigate by file. So you can click on files in here. But it also lets you navigate in all these other ways along the top. Like you can navigate by searching. You can navigate your breakpoints in the debugger. You can navigate through all your old builds, the outputs from your old builds, et cetera. So you're really gonna get used to using the Navigator to move around inside your app, find the things you want, et cetera. Now, in the right-hand side, there's this gray area here. This is called the Utilities window or the Inspector sometimes we call it. And we really don't need that very much for our first couple of lectures. So I'm actually gonna use this opportunity to show you how to hide it. If you look at these three buttons in the upper right, they let you hide and show these panes. And I'm actually gonna leave Utilities hidden because like I say, we don't need it much. You see there's a third button here. This actually brings something up from the bottom. This is your debugger and your console output down here. I tend to leave those off screen just because they're wasting screen real estate if I'm not actually debugging. Also, they'll often come up automatically on their own when you for example, hit a breakpoint in the debugger or some output comes out on the console. And this area is the main editing window. And you can see that it's currently split in two. And you can split this area in many, many ways if you want. That's what this little plus button over here does. It lets you add another, a third editor on the right. You can even add an editor on the bottom if you'd like your split to be top-bottom, et cetera. So that's kind of our tour of the Xcode UI. Of course there's tons of menu items up here and we'll learn about them as the quarter goes on. But let's look at our main editing window and see what we got. We got this ContentView.swift you see right here. This is a Swift file. So this is your first look at the Swift language. And I'm gonna start with this little piece of code. And what this little piece of code does is it provides some glue between your code that you're writing and this little area on the right which is called the Preview window. See, it says automatic preview updating paused. And of course we have the simulator. We can always run that to see what our apps are gonna look like. But we also can see what's happening in our UI, kind of in real time, approximately, by hitting Resume here. And what this is gonna do is compile our code and essentially run it right here. It looks like a little iPhone. Notice this is not in Dark Mode so the text is black on white. And that's automatic, the SwiftUI takes care of that for us. And we can resize this too. Maybe make it over here, it'll have less space. And this code that is essentially gluing to it, I'm not really going to pay much attention to it. In fact, I'm going to move it out of the way. And when you make a big edit like this or you're moving a lot of code around, sometimes this preview will pause. You see, it's paused again. No problem, you can just resume. But a lot of changes you make, like if I change this "Hello, World!" to be "Hello There, World!" you could see it's actually changing it in real time. So it depends on the kind of change you're making to your code as to whether it will update in real time. And again, you can always run the simulator. Especially if you wanna see it on lots of different devices and things like that. The simulator is always available to you up there. So let's take a look at the Swift code. This is the entirety of the Swift code that's specific to our app. There's a little bit of boilerplate up here in these two delegate things. We'll look at those later but this is really pretty much all there is when it comes to doing this Hello, World right here. So let's look at the Swift code in detail. First we have this import SwiftUI. I'm sure you can all imagine exactly what that is. It's like include or import in other languages. This imports what's called a package in Swift. This package is SwiftUI. So that's the big ol' package that Apple provides that makes all this SwiftUI stuff work. So anytime we're doing UI stuff, we're always gonna be importing SwiftUI. Sometimes we're going to be writing code that is not UI stuff. In fact, intentionally UI independent. And in that case, we will not be importing SwiftUI. We'll be importing a different package called Foundation. Foundation is kind of arrays and dictionaries and strings. And the SwiftUI package depends on Foundation. So if you imported SwiftUI, you get that one too automatically. So this whole thing is really just three lines of code here, not including the curly braces. So let's see what it is. This first key word here. Notice all the Swift keywords are in magenta. We can always tell the difference between Swift and the things we do. So this is a struct. This is just declaring a struct. Now, I'm sure you know a language that has structs. C and C++ and these languages all have struct. And the struct in Swift, just like the other languages, is a container for some variables. But structs in Swift are much more powerful than that. Structs in Swift not only can have variables, but they can have functions and they can also have behaviors which we're going to see very clearly right off the bat here. So this struct, its name is ContentView. So the name of our identifiers are in green. And Swift's identifiers are in purple and the keywords again are in magenta. So the name of this struct is ContentView. And this is a very interesting little part of this struct's declaration. It essentially means that this struct is going to behave like a View. Or some might say it's going to function like a View. Or some would even say it "is a" View. Although if I use the statement that it "is a" View, some people will think that's object-oriented programming like the superclass or something. But it's not. This is not object-oriented programming. This is functional programming. And that's why we may be more likely to say something like a ContentView functions like a View or it behaves like a View. And this is super important. This behavior, View, is crucial to how all of SwiftUI works as you will see. We are going to be using so many Views in the next couple of lectures, you can't even imagine it. And we're gonna talk all about the concepts behind this behavior specification using slides at the beginning of lecture two. In the meantime, we're just going to understand that this means that ContentView behaves like a View. A View is just a rectangular area on screen. So here I've put my mouse on this Text and it's selected it over here in the preview. And you can see it's put this blue border around that Text. That blue border is bordering this View. So a Text, it also behaves like a View. It "is a" View. And so you're seeing it right here. So that's all Views are. They're rectangular areas on screen, both for drawing and also for multi-touch. For swipes and taps, pinches, those kinds of things happen in these rectangles. So that's what a View is. So when something behaves like a View, it's a rectangular area on screen. So our ContentView which behaves like a View is this entire rectangle. The whole rectangle that fills the screen is our ContentView. All right, if you are somebody, some struct like this ContentView, and you want to say that you behave like a View, you must in your struct have this var right here, this var called body. I'll select it for you here. So here's the declaration of var body. So Swift variables, they have the keyword var, short for variable. Although we don't tend to call variables inside a struct like this vars. We call them properties. So you're gonna hear me use the word property all the time. It means a var inside a struct or a class. Also, if we're doing object-oriented programming, we call them properties. So the syntax for a property, really easy, you got a var. This is the name of the property. Again, it's green 'cause it's something we chose. And this right here is the type of this property or of this var. Now, this is a pretty interesting type even in Swift. Because it's got a little magenta keyword in the middle of it. Normally this type might look like this is an Int or maybe it's a String. Or it might be a Boolean value or it's an Array or something. But in our case, it's this some View which is kind of a interesting type. What this essentially means is that the type of this variable, this property, is any type, any struct, as long as it behaves like a View. As long as it is some View. So that's what this kind of odd thing means right here. So if you wanna behave like a View, you have to have a var called body that also is another thing that behaves like a View. And you might be kind of thinking, "What? "That's weird. "So to be a View I have to have a var "that is another View as my body? "That makes no sense." But you could kind of think of Views as Legos. So of course there are some brick Legos, Legos that are like the basic thing like Text. Text is like a brick Lego. But then you can put Legos together to make something else. And that thing that you put them together, you could call that a new Lego. It's like a combined Lego or a more powerful Lego. For example, if you were building a Lego house, you probably would make little Lego furniture. A Lego couch, a Lego kitchen table, all those things out of other Legos. And if you consider that couch and the kitchen table as Legos, then you're putting those together to make the house. You could even then have the house be a Lego that you put together to make a Lego neighborhood. Or the Lego world, Lego universe. That's the way we wanna think of making these Views. Now, the difference between a Lego and a View is that there are special kinds of Views that are used to combine Legos. We've got these basic brick Legos like Text. And then we've got other kinds of Views that are combiners, Lego combiners, View combiners. And you're gonna see us use those in detail here. That's why our body just returns one single some View of some sort. It might be returning a combiner. If it returns a combiner View, then it could have tons and tons of Views inside that are combined. Now, this is also a kind of an interesting var here in that it's got this curly brace thing after it. This var, its value is not stored in memory. Instead this var is computed. So every time someone, namely the system, asks for the value of this var, this code in this curly brace gets executed. And whatever the value that's returned, that's what the value of the body is. And every time it's called, it gets called again, it's constantly calling this thing. Now, this code in here seems kind of minimalistic but it'll probably make more sense if I put this keyword back. This keyword, return, you see is magenta. It's a Swift thing, it means return. Return a value from this piece of code, this little curly-braced piece of code. And it wasn't there because Swift loves to leave things out and make it so you have to type as little code as possible. You're gonna see that later. And so if you have a one-line function like this that returns a value, so that's what this is, a one-liner returns this Text thing. Then you can leave this return out. It'll just infer that. But I'm gonna leave it in because I want you to realize that that's what's going on here. We have this little piece of code, it returns this Text thing. And that's going to always be the value of body. Anytime someone asks me for the value of body, it's gonna execute this and return this Text right here. Now, this return type, remember, is some View. So if you're saying, "It's returning Text. "What's going on here?" Well, of course Text, it says it behaves like a View. So this is "some View". In other words, somewhere off in Apple's code there is some line that looks like this. "struct Text behaves like a View." And that's why we can return a Text as the value of body 'cause it's some View. In fact, we could even just type Text right here. That would be valid because to behave like a View, your var body just has to return something that is a View. And of course, Text is a View. So why don't we type Text? Why do we do this some View? Well, as our body of our View gets more complicated and we start using these View-combiners and things like that, it's gonna be constantly changing. Which kind of View we're returning. Right now we're returning a Text. But eventually we're gonna be returning View-combining Views. And we wanna let the compiler figure it out for us. So this some View is basically saying to the compiler, "Go look in my code right here. "Figure out what it's returning. "Make sure that it behaves like a View. "And then use that as the type of the body." You're gonna find as we go through this course that Swift is what's called a very strongly-typed language. That means every variable has a specific type. It's not a language like JavaScript or other where it's like well, the variable depends on what you assign to it. That's when it gets ... No, in Swift, every variable has a specific type and always has a value. That's just fundamental to the way Swift works. So let's look at this Text right here. We create this Text. Anytime we're creating anything, any struct, so a Text is just a struct that behaves like a View. And anytime we create one, we in parentheses give it whatever information it needs to create itself. Now, obviously for a Text, it needs the string that it's going to use to create itself. and that's why you get this "Hello There, World!" And notice if I edit this, it's not changing this. That's because it's paused up here. But if I resume, then it's going and now I can for example type, and it's tracking it. So this is always the way we create new structs of any kind. We put parentheses after it and then we give it whatever arguments it wants. And some things when you're creating them, can take different kinds of arguments depending on what they are. And you're gonna see that pretty soon here. So let's see if we can start building a UI that looks a little bit more like a card. Right now it just says, "Hello, World!" Let's remind ourselves what that's gonna look like. Kind of a screen capture of the game that I showed you earlier. And our cards, they kind of have these rounded rectangles around the edge, you see that? And this is a line. Then kind of a white background. They have an emoji on the front. So let's take care of each of these things. Let's start with the emoji. That's gonna be really easy 'cause an emoji is just a piece of text. So I'm gonna delete that. I don't know if you know this but in any Mac app, if you go to the Edit menu, go down to the bottom, there'll be Emoji & Symbols. You see there, I can get an emoji. You can even search ghost. There it is, ghost. Let's double-click on it. Now we have a ghost. And sure enough, there's our ghost. He's really small. Although we can zoom in and see him there. And we're gonna have to fix that he's so small. It's not gonna work for our cards for him to be so tiny like that. But one thing at a time. The next thing we need is our rounded rectangle. So we need this rounded rectangle, it goes up there. So let's comment this Text out. So I'm doing Command slash by the way there to comment that Text out. And instead put our rounded rectangle in there. Luckily, Swift has an awesome thing here. RoundedRectangle which is exactly what we want. And again, just like when we created Text, we have to provide some information to create a RoundedRectangle. You can by the way, easily find out what that is by just doing the open parentheses and Xcode will show you the options. Now, RoundedRectangle, there's four different ways to create a ZStack. You can specify the corner radius or the corner size, width and height of the corner. I'm gonna do the cornerRadius. So you can hit Tab here or you can double-click on this. Then it wants the radius and this is in points. So I'm gonna do 10 points. A point is the same as the point size of a font, like 10-point font. It's also, just to give you an idea of the size of a point, a typical iPhone is maybe four or 500 points wide and maybe seven or 800 points high. An iPad might be 1,000 by 700 and something. Now, I'm being intentionally vague here because we never write our code trying to know what the sizes of screens are. SwiftUI helps us write code that works on any size screen. It just adapts and adjusts to any size screen. You're really gonna see that with our Memorize game when we start having rows and columns of buttons. On big screens they're gonna be large. When we're in portrait mode it's gonna kind of resize things one way and landscape another. So we always wanna write our code so it works in any size screen. But that's the approximate size of a point to give you an idea of a point. Now, one thing that's kind of strange about this is this right here. It has put a label on this piece of information that it needs to create a ZStack. And that didn't happen with Text. This doesn't say the string or something like that. Text doesn't have one of these. But this is actually the norm. It's unusual to have something like Text where there's no label here for an argument. You have to go out of your way to do that. And I'm gonna show you how to go out of your way although we're not gonna do that very often. Instead, when we create things, when we pass the information, we almost always label each piece. And this is true when we call functions as well. So one of the things that maybe takes the most getting used to in Swift is that almost all the arguments to all parameters of all functions are labeled. And this is something that was inherited from Objective-C. It's actually quite nice because if we didn't have this and we had ZStack 10, I mean, is that the width and height or what exactly is that 10? But as soon as we put this cornerRadius in there, it's like all of a sudden, "Oh, I'm creating "a ZStack with a corner radius of 10." It's kind of hard to see if our rectangle is rounded there. That's because I actually have this line of code selected. So we've got this blue line around here. But if I just click away, then you can now start to see the ZStack there. And if I zoom in, you can see the ZStack quite clearly. Obviously, we're gonna have to do something about the visibility of this. Our cards don't want to be smashed up against the edge and they don't want to be black either. But first things first. We'll get to that in a bit. The next thing I wanna do is combine this RoundedRectangle and this Text together. Because remember, our cards have both a RoundedRectangle and a Text stacked on top of each other. And I'm gonna have to do that by returning some View. So the View I'm gonna return here is called a ZStack. So a ZStack is just a struct. It behaves like a View just like RoundedRectangle is a struct that behaves like a View. So is Text. Actually, so is our ContentView. A ZStack does need an argument to be created here. The argument we're gonna use is content then in curly braces The list of the Views to stack on top of each other. And obviously we don't need the returns in here. So this is the list. RoundedRectangle in the back, Text in the front. First thing in the list and this is two items but we could have five or six or seven items all listed in a row and it would stack them all on top of each other. ZStack is just some View. And in fact, this return type here would be some sort of ZStack here instead of a Text or a RoundedRectangle or something like that. But Swift is going to automatically realize that it's returning this. And so it's gonna do that for us. This kind of combiner View is really important obviously to building complicated Views. And we're gonna build our own combiner View next week. So you're really gonna understand how these things work. Now, we're getting there. But there's still some things, we'll kind of resume here, that this doesn't look much like our card. First of all our cards aren't filled with black. They're kind of stroked with orange around the outside. Also we have a problem where it's hard to see the edges 'cause they're all the way out to the edge. So let's fix both of those. First of all, let's stroke this instead of filling it. And the way we're gonna do that is we're going to call a function on RoundedRectangle called stroke. RoundedRectangle, we know that it behaves like a View. It has to behave like a View or we wouldn't be able to put it in the ZStack like with this Text. But RoundedRectangle also behaves like another thing which is a Shape. Other Shapes are Circle, Capsule, bezier paths, these are all Shapes. And Shapes can all be stroked by calling this function on them called stroke. And you can see that it's exactly what happened. It stroked a line around the edges of this RoundedRectangle. And stroke is an interesting function in that it returns something. Can you guess what it returns? It returns a View. It returns something that behaves like a View. And it has to because whatever this is has to be a View. Otherwise again, we couldn't put it in the ZStack. So this right here is like a stroked RoundedRectangle View or something like that. So there's two Views still in the ZStack. A stroked RoundedRectangle is one of them and Text is the other. So this pattern of calling a function on a View or a Shape to return another View is very common. This is the first time you've seen a Swift function call. Really simple, it's just dot and the name of the function. And then in parentheses, any arguments. Now, stroke actually can take arguments but it works perfectly fine without its arguments. Without its arguments, it just kind of does this default behavior of stroking kind of a one-point line around the edges of the Shape. So what about the color of this RoundedRectangle's edge here? It currently looks like it's kind of either maybe dark gray or something like that. What if we want that to be orange? So how do we do that? Well, this is a View right here, some View. And all Views, anything that behaves like a View, you can call the function on it called foregroundColor. It takes an argument which is a Color. I'm gonna use the Color orange. And that will set the color that it uses to draw to be this Color, orange. And sure enough, look at that. It's changed it to orange right there. Now, this function can be called on any View. We could call it on Text. You can see, no errors. Of course, it doesn't really help with Text 'cause it's an emoji. But if Text were the letter X instead, you'd see that that's an orange X right there. Again, it doesn't make sense to call it there. The other thing we could send this orange to is our ZStack. What does it mean to tell this View to have its foregroundColor be orange? ZStack is just a View. It behaves like a View so it has foregroundColor just like every other View in the world. And what it means for this is "Tell every View "inside of me to use foregroundColor orange." So this works as well. Look at that. See, that's orange. Again, if we change this ghost to an X, that X would be orange as well because the ZStack is essentially setting the environment that all of the Views inside of it are going to use to draw. Now this can be overridden. We could then say, foregroundColor(Color.blue). And then we'd get a blue one out here. But this would still be orange. Because I only overrode this foregroundColor blue for this View right here, this stroked RoundedRectangle. I didn't override it for this X right here. So by scoping the calls to these functions, so we can control what color things are. And this is really valuable for the ZStack because we're eventually gonna make this ZStack so it does the front of the cards, the back of the cards, everything in all those cases that wants to be orange. So we want orange for everything so it's really nice to be able to set that for the entire ZStack. And we usually put these modifiers for these combiner Views on a line by themselves, just to make them stand out a little more. Whereas we might leave them on the same line here. Although occasionally, we would even put these on a separate line as well. But we're not gonna do it here, but you might. It's kind of a style thing. However you think your code is most readable and most understandable. So we're making good progress on our card here. One thing, it's really hard to see the edges here. We kind of want some padding around this edge. And we can do padding with another View function called padding. And here notice when I put padding around my Text, oop, it popped out, it got some padding around it. Now, I could do the same thing over here to my stroked RoundedRectangle since it's a View. padding, ooh, that's nice. You see, it put padding around there. Again, I could even put the padding around my entire ZStack. It put the padding around the whole ZStack which is probably what I want 'cause I don't wanna have to put the padding on every single RoundedRectangle separately. Like the RoundedRectangle we'll use for the back of my card eventually and things like that. So this is nice to be able to put these on the outside. Now, notice the subtle difference between padding and foregroundColor. Padding pads the ZStack. The entire ZStack gets some padding around it and everything in the ZStack is drawn in that little padded area. Whereas foregroundColor doesn't really make sense for a ZStack. A ZStack doesn't have a color. All it does it contain other Views. So it gets passed down into the environment for all the Views on the inside of it. All right, let's see, we've been looking in our preview here. But let's go take a look in our simulator to see how this looks. And ah, kind of interesting. It's working but it doesn't look very good here. That's because our simulator's in Dark Mode. And really we want this background to be white. Even in Dark Mode we want it to be white. We don't want it to be black cards on black background. Let's say we want it to be white. So how would we make that white? Unfortunately, there's no function you can send to a RoundedRectangle that says stroke it with one Color and then fill it with another Color. But that's no problem because we're in a ZStack right here. So I'm just gonna create another RoundedRectangle, and this one I will fill instead of stroke. And remember, it's in the back of this. And resume right here. Now, that's gonna fill it with whatever the foregroundColor that's set as the environment here, so I can't even see. But luckily fill takes an optional argument which is the Color to fill with. And that overrides this foregroundColor here. So now this is white. If I click on here, it is filling it with white. And there is that orange border, kind of hard to see. It's only one point wide, especially near the black. It's really kind of hard to see. So let's fix that. A stroke I told you could take other arguments. One it can take is line width. So let's make it a line width of three. Again, we could resume here to see it in Light Mode. There it is. And we can run over here to see this in Dark Mode. And by the way, the dark and light mode of your simulator can also be controlled right here. You see this little button down here? You see this Interface Style? You can turn that on and go from Dark Mode to Light Mode. Now if you go back to your simulator, it's in Light Mode. That's really kind of a cool way to switch back and forth. So we are making some great progress here. Our little emoji is still really small. I'm gonna make it bigger. I can do that with Font. Font, Font.largeTitle. Now I'm using a pre-canned Font here called largeTitle. There's some other ones called subheadline and body and other things. largeTitle happens to be the largest one I can find. If we resume, you are gonna see it's gonna make our emoji larger. Still not large enough. Next week we're gonna learn how to scale the Font to an exact size that fits nicely in our card. But for now at least it's getting a little bit larger. One of the reason I did this is I also wanna show you that this Font, while it seems like this would be a Text only thing, it can actually be put on the ZStack and it will set the font environment for all Texts. So if I had multiple Texts inside the ZStack, then it would use the Font, that Font for all of them. And you could easily imagine building something that had multiple pieces of Text and you want them all to be the same Font. And so setting it on the ZStack is a really cool feature for that as well. So I've kind of got the card somewhat looking like I want. Now I want multiple cards 'cause I only have one card here and I'd love to have multiple cards. So how am I gonna get multiple cards? I'm gonna do that by returning a different kind of View, another combiner View. This is called a ForEach. Now, a ForEach, of course it takes arguments. The first argument, it takes two arguments. The first argument is like an iterable thing of some sort. We'll talk about that in a second. Then the second argument, just like a ZStack, is content. And that content is going to be this ZStack. What ForEach is gonna do is it's going to iterate over this iterable thing. And for each of those things, it's going to build one of these Views. So it essentially is a way to make multiple Views. Kind of a View replicator or a View iterator if you wanna think of it like that. So what is this iterable thing we're talking about here? Well, usually this is going to be an array. So you're gonna give it an array of things and it's gonna, for each of the things in the array, it's going to create a View. I'm not gonna use an array here. We'll eventually switch to using an array but I'm using another iterable thing called a Range. So this is the Range from zero up to and not including four. That's what this Swift syntax right here means. It means a Range from some lower bound to some upper bound, not including the upper bound. If you say dot-dot-dot, then it includes the upper bound. But I don't wanna include the upper bound. So, this is going through for zero, one, two, and three. It's going to create this ZStack in here. So it's a repeater. Now, one thing that's also cool, so it's got the same thing where it's got the content. And this could be a list of Views. We only have one View in here, it's a ZStack. But we could have multiple things. Just like in the ZStack, we have multiple things. The ForEach, its little curly-braced thing could have multiple things. And one thing that's kind of cool also about the ForEach is it actually has this little thing here which we're gonna talk about this "in" meaning at some point But essentially this is the iteration variable. So in this case, this index is zero. And then it's one and then it's two and then it's three as it makes the four of these. Now, one thing about ForEach, you'll see in the preview it actually made four separate previews of it. And that's because ForEach is not a layout View. It's not like ZStack. ZStack positions the Views on screen on top of each other from back to front. ForEach doesn't do that and if you run, if we were to run this in simulator, it would probably put the View somewhere because it obviously can't simulate multiple iPhones. But really the preview is actually showing more of what's happening where it's creating four of these Views. So it's showing you each of the four Views that it's creating. Which is really cool for debugging. It's like, "Oh yeah, this is the one now." Our four Views are exactly the same so not so interesting for us. But what we really wanna do is put all four of these on the screen at the same time. And for that we need another stack. Now, we don't wanna stack them on top of each other and we wouldn't be able to see the ones in the back. Instead we want a different one which is we're gonna return an HStack. Our HStack just like ZStack has content. And you put in the content the things you want to arrange like this. And HStack instead of arranging things from back to front, it arranges things horizontally. That's what the H means, from left to right. So look at that, left to right, it arranged them there. Now, I told you that Swift likes to not type things that you don't need to type. And that return I can still remove because believe it or not, there's still only one View being returned. Being used as the value of this body, some View, it's one View. It's a HStack of ForEaches that make ZStacks that's padded. It has a foregroundColor and a font. That is the one View that's being returned. Of course, HStack is a combiner so it's combining things. And ForEach is a combiner and ZStack is a combiner. But those are all inside the HStack. Now, notice also that the HStack when it sees a ForEach 'cause this could be a list, we could say Text("hello") So it puts hello in there. And then this ForEach is kind of like listing each of these ZStacks separately. That's why it does this. So each stack is smart. It knows if it has a ForEach inside, then it wants to lay out each of the ForEach things separately. Now, another thing to notice here is the spacing. There's a little bit of space between these things. Is that this padding right here? No, that padding is this padding around the outside of HStack 'cause the padding function is called on the HStack itself. That spacing is actually something the HStack is doing and it has an argument that lets you set that. So here I've set the spacing to zero. Or I set it to 50 or I leave it unspecified. A lot of times we're leaving those things unspecified 'cause we want this spacing to be standard spacing. The same in all apps. Same thing with padding down here. We didn't specify anything here but in padding you can actually even do padding on the top is 100. Or padding is 10 on all sides. But usually we go for standard padding as much as we can. Standard padding and spacing. Now, I told you, remember we took away this return. There's another thing we can take away. In Swift, if the last argument to a function or the creation of something, so here's ForEach. It has two arguments. This and then it has this as its second argument. If the second argument is a curly-braced thing, then believe it or not, you can get rid of the label and put it outside of the function call. So here's the ForEach. So basically the creation of the ForEach. And here's its second argument. It's floating outside of the parentheses. So this seems kind of crazy. But actually, look what it makes our code look like. I'm gonna do that for all of these things 'cause this only has one argument. So I'm gonna get rid of that. And I'm gonna do the same thing here for my HStack. So that looks kind of a lot cleaner. You don't have those contents in there. And even more than that, if you have no arguments, in that case where you have the little curly-braced guy hanging out there, you can remove that as well. So that cleans up our code considerably. And of course we're doing these ZStacks and these HStacks all the time. Now, if I wanna put this spacing back, I could. Spacing, zero, perfectly fine. It's still the first argument. This is still the second argument to the HStack there. So it's kind of a little clean up. So our code is looking pretty clean already making these four cards. We can clean it up even a little more by factoring out some of our code. This ZStack here is essentially one card. Wouldn't it be cool if I just created a new struct called a CardView which also behaves like a View? Which means what? It has to have a var body that is some View. And in here we just return some View. I'm gonna return a ZStack. Cut that out of there, paste it in there. And then in ContentView here, I'm just gonna create a CardView. So this is a way we can use the Views to factor out our code to do encapsulation. To make our code look even simpler and nicer. Somebody looking at this ContentView, it's really easy to see what this is gonna look like. And similarly down here. Simple to see. Now, the last thing I'm gonna do here in this lecture is do fronts and backs of cards. 'Cause right now, we only have the front of the card shown. We don't ever show the back of the card. So how would we draw this back of the card? Well, it's just a ZStack as well but it's filled with the foregroundColor. So we could actually go ahead and just copy and paste here a RoundedRectangle to be the back. And for the moment I'll comment this out. And of course we don't wanna fill with white. We just wanna fill with whatever our environment Color is. Which it's going to get from here. Notice that this foregroundColor not only applies to everything in the HStack that ForEach passes it on down so that it applies to the CardView which passes it on down. Which applies to the ZStack which passes it on down so that it applies to all these things. So now if we resume. We can see backs of cards. See, here's the backs of our cards. Right here, RoundedRectangles that are just filled. But of course, really, we want to have fronts of cards and backs of cards somehow be conditional. Or the card is either face up or face down. So we need an if-then or something. Well, the amazing thing here is that this ZStack, which allows you to put a list of Views in here also lets you put ifs. So you can put simple ifs like if, let's say, isFaceUp for now. And we'll put these three things in the isFaceUp case. And then, otherwise if it's face down, then we will put this right here. So we can actually put an if. Now, we got all kinds of errors and warnings here because I just made up isFaceUp. That's not a thing. I just made that up. What do I want isFaceUp to be? Well, I'm just gonna have it be a var. It's gonna be a normal in memory var. It's not gonna have any curly braces or anything on it. It's just a normal in memory var. And you see that fixed up all of our code down here. CardView is perfectly, internally self-consistent. It's referring, it's saying if my isFaceUp var is true, then this way, otherwise this way. But it did cause an error up here. And what is this error about, missing argument for isFaceUp? Why is it saying that? Well, I told you earlier. Swift, all variables, it's strongly typed and all variables have to have an initial value. So we could set an initial value by just saying it equals false. And if I say that it's going to fix this error right here. Face up is false, does that. And if I change thisFaceUp to be true and resume, now we get face up cards. So that's one way to set this. And we often will set this if it makes sense. But if we don't set it in here, we don't give it a default value, that's when we get this error up here. Because Swift is saying this CardView that you're trying to create right here, it has an uninitialized variable. So you have to initialize it. And here's a cool feature. If you click on this little red button of an error, a lot of times you'll get this little Fix option. And the Fix will fix the error for you. Now, it's not always gonna fix it exactly the way you want so it doesn't work all the time. And there's not always a Fix option. 'Cause sometimes it can't figure out how to fix what you're saying. But here it does work. If I hit Fix, it actually adds an argument to the creation of CardView to set this variable isFaceUp. So if I say false right here, it's going to initialize this variable. Let's resume. And there it is. False, face up is false. This gets initialized false. All these are not face up. Now, hopefully this pattern is looking familiar to you. It's the same as this pattern. Here I'm creating a ZStack. It has some var probably. Not necessarily because there are other ways to pass these variables. But it might have a var called cornerRadius. And so it's forcing us to set it, to define it 'cause we can't have unset vars. This is the same kind of thing. Okay, that is it for this lecture. In the next lecture, I'm gonna actually start with some slides, some conceptual stuff. Then we'll get right back to this demo. Normally I would tell you a little bit more about what's gonna happen in the next lecture. Except for that I'm releasing lecture two along with lecture one. So if you really want to see what's in lecture two, just go watch it right now. - [Announcer] For more, please visit us at stanford.edu.
Info
Channel: Stanford
Views: 330,736
Rating: undefined out of 5
Keywords: SwiftUI, Xcode, iOS, iPhone, iPad, Swift, Stanford, CS193p, coding, iOS programming, Memorize, declarative user-interface
Id: jbtqIBpUG7g
Channel Id: undefined
Length: 62min 20sec (3740 seconds)
Published: Mon May 18 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.