Understanding SwiftUI ViewBuilders

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi my name is stuart lynch and in this video we're going to be taking a look at the swift ui view builder parameter that lets you construct views from closures if you don't know what that means think of it as your own custom implementation of something like an h-stack or a v-stack in that you can define how you want your views you provide in the closure to be presented we'll start off with a basic use case and then explore two practical examples including container views and a heads-up display view before i get started please make sure if you enjoy this video to leave a comment below and give it a thumbs up and subscribe to my channel make sure you ring the bell be notified of new videos if this is something you want to learn then keep watching for this video i've created a starter project that is simply a tab view containing three views where we will be implementing our three examples as i mentioned we'll be starting with the basics so that you can understand how to implement the view builder and i'll follow that up with an example of creating and using a custom container view and then we'll create our own heads-up display or hud that will present some kind of alert and then disappear so let's get started in the first tab i'm going to create a button where the label will either be a rectangle or a circle depending on the state of a boolean value so let's start by creating that boolean value that can change as a state property we'll call it is square and set the initial value to true now we can create our button with the action that will toggle the value of is square and for the label we can use an if-else clause to decide what kind of view we want to present as that label so if is square is true i'll create a rounded rectangle with a corner radius of 10 i'll set a fill of color green and i'll set a frame that has a width and a height of 100. for the else clause i'll create a circle that has a fill color of red and i'll set the frame to a width of 100. if i preview now in the canvas as i tap on the object the shape and colors change as expected now one of the design principles of swift ui is small view structs so why don't we extract our label into a utility method as an extension of our view so first we'll create the extension for basics and now we can create a function that has no arguments but returns some view and then we can cut out our labels if else clause and paste it in there and replace the cut with a call to that function the function however generates an error it declares an opaque return type but has no return statements in its body from which to infer an underlying type well the views are different and even if i did add returns we'd still get an error well there's several ways to solve this problem i could enclose this in a group and the error goes away i could also as i said before add a return before each of our views but i'd also need to enclose these in any view to perform type erasure and that way it gives both of our code branches the same return type as you can see this is working fine too but there is a better way and that is simply to decorate our function with the view builder attribute so let me remove the return and the any view enclosures and i'll add the app view builder to our function definition again this is working as we hoped for completeness sake i'd be remiss if i didn't present an option that most of you would have probably used and that would be to instead of creating a utility method to extract this as its own view and then pass in the boolean value as a binding so let me comment this out first first i'll create the view and now a binding to the is square boolean value and then for the body i can paste in the code and then for the label i can simply use this view with our is square property as the binding this works too which method you use is up to you i prefer to use the view builder option for generally isolated views like this one and to extract to a full view if i want a reusable component on this second tab i'm going to take a look at the at view builder in the context of a container view so what's a container view well in swift ui you use them all the time this is what a v-stack and an h-stack is let's embed our text view in a v-stack and add a second text view with some text if i control command click on our v stack i jump directly to the definition and in the definition of this struct we see that it's initialized with our content as a closure and that closure is decorated with the app view builder parameter we can use this information then to build our own container views with viewbuilder it's simply a struct and we can give it any name like v-stack or h-stack but i'll call mine my container and it will have to conform to the view protocol in addition to that however we'll be providing our struct with some content which must also conform to the view protocol so we can use generics here by specifying a placeholder that instead of giving it the normal t designation we'll use content with the capital c and make sure that the content also conforms to the view protocol and then within the body of my struct we'll create a property that is of type content capital c which is our generic type need an initializer for that content so we can specify that the content will be a closure that will return our generic content view and then we can set our content to this content closure the one thing we must do in addition however is to also add the at view builder parameter just as we did for our first function so that whatever we return as content will be able to be handled now because the struct itself conforms to the view protocol there must also be a body property that returns some view so let's just return the entire content because it conforms to a view protocol this then is the basic building block for our container view this content is whatever i pass in via the container closure in our vstack example it was these two views so instead of a v stack let's change our container view to a mac container this means then that we can treat whatever we pass into this struct as a single entity we still have the same limitation that we had on v-stacks and h-stacks being that we are limited to 10 views but we can play around a bit here what if i wanted to embed this container itself inside a v-stack this does exactly what you'd expect it to do it embeds both of the views that were passed in into a v-stack and we get the same result as we had initially let's add some padding to our content a padding of five how about adding a background color to the v-stack of a rounded rectangle the corner radius of 10 and a fill of color green we can also set a foreground color to white and then how about adding a shadow with a radius of five now at this point you may be asking yourself what's the advantage to this i could have just as easily applied these modifiers to our original v-stack however we now have a reusable container view that i can use anywhere so what you say you could have done that with your own custom view but could you have done this how about we add another view to our container but this time not a text view how about a button and for the label let me set an image a system image of star if my container was a simple view we would have had to specify exactly what number and type of views we wanted in that v stack with the view builder we can decide at the top level so as long as we don't exceed the 10 views and the modifiers that we apply to our content will apply to all the views that we have within our closure currently this my container view only has a closure but you may recall that v stacks and h stacks which are view builders also have optional arguments like alignment and spacing so what if we wanted the background color to default to green and the foreground color to white but have the option to provide a color for the background and or the foreground well that's easy we just add new properties to our my container one called bg color of type color and the second fg color of type color and then in the initializer we can assign our default values of green for bg color and white for fg color and then instead of the hard-coded fill in our backgrounds rounded rectangle we can replace it with bg color in the hard-coded white foreground we'll replace with fg color this doesn't change what we have in our canvas preview let's test this out in our original body by embedding our my container view itself in a v stack and add a second my container as the second view in the v-stack this time though for the second one i'll set a background color to red and a foreground color to yellow and for the views that i'm going to pass in i'll have one that will be a text view and for the second i'm going to pass in a rectangle with a fill of color yellow and i'll restrict the frame to a width of 200 and height of 50. now we have a pretty flexible container view using a view builder so let's move it out now to its own file so that we may be able to use it in our final example i'll cut out the view from here and i'm going to create a new swift file that i'll call my container i'm going to have to change the import to swift ui and i'll paste in the code now if you wanted to have a preview for this view you can do that by creating your own preview provider and then in the preview just submit two random views but make sure you enclose them inside a my container view builder for our final example we're going to create a container view that we can use as a heads up display so let's start by creating that generic view builder as we did in the previous example i'm going to call the struct hud view and it'll conform to the view protocol and we'll be providing it with some content that is generic in nature but it too must conform to view and then the content property is of type content and we can initialize that as a closure that returns this type and we'll have to make sure that it uses the view builder decoration and again because our struct conforms to the view protocol we have to have that body property that will simply for now display the content provided in the closure now this is how we start every view builder that you want to create so i've created a snippet for that myself so that all i have to do is type sw underscore vb and then provide it with a view name snippets are great and if you're interested in the power of snippets i recommend that you watch my video on that topic i'll leave a link in the notes below so for now let me just embed that text view in our original body inside my new hud view container and while we're at it in our hud view itself let's embed the content inside a my container container view now for my heads up display we want that background color to be a secondary system background and the text which is our foreground color we want it to be secondary label so that this is going to ensure that it's going to appear nicely in both light and dark mode and then next i want to embed the entire my container inside a v-stack itself with a spacer to push it up to the top now back in our current views body i only want this hud view to appear when some action has completed or if i tap a button so that means we'll need some kind of boolean state variable that we can change so let's create one that i'll call is showing hud and i'll initialize it as false so in the body we'll need to create a z stack so that this view is only shown if is showing is true and so in the second item of the v stack i can create a button that will toggle that state now when i tap that button the heads-up display is displayed that's not too exciting i could have done that with any view what i want to happen is i want it to slide down gradually from the top and i also want to dismiss itself after a short period of time but there's another problem and that's the way i've positioned this hud view in my z-stack it's behind the second view so if this view were more than just a simple button like say let's say we embedded this inside a z stack itself and added a color as the back view of this button now when i tap on the button our heads up view is hidden behind the green color view now i could have fixed this by moving the if clause below the other view to make it our first one in our z-stack but better than that i can go directly to our view builder and say that i want this hud view's z-index to be 1.0 this will force it to be always on top inside the container now i can work on the animation by adding a transition and an animation now the transition will be a move where the edge is from the top and for the type of animation i'm just going to use a simple spring animation now when we tap our button it springs down from the top and when i tap again it goes back up we're almost there what i want to happen now is that after a short delay i want it to disappear without me having to tap that button this means we're going to have to toggle that state variable somehow so we'll need a binding to our view builder for that and add it to our initializer now because is showing hud is a binding when we initialize it we'll initialize it as a binding to a bool and then in the body of the initializer we don't use a dollar sign instead we use an underscore when assigning our issuing hunt parameter to our binding with that then we can add an unappear method for the v-stack that we'll use a dispatchq.main.async after and with that we can set a deadline of 3 seconds from now and then set the issuing head back to false inside a with animation block and that then will dismiss the hud after three seconds then the last thing we need to do then is to pass in that binding to our state variable when we create our hud testing this now we see that the hud displays when i tap the button and then after three seconds it moves back up out of view now remember that our hud view is a view builder which means that we can pass in more views to our hud view to provide more information for example we could just add in another text view now i chose to use my my container view within the hud view view builder so if you want to use this in your own project you'd have to either add this hud view to your my container view file or create a second file for hud view and include both alternatively you could do a better job with the hud view by adding all modifiers to the content within the hud view itself and have no dependency on the my container view the sky's the limit for you now and you can create some pretty powerful reusable views now if you're interested in taking the hud view builder even further i recommend that you read this post from federico zanatello's five star blog this takes the concept even further to reduce your code you can always learn great things from the swift community
Info
Channel: Stewart Lynch
Views: 3,254
Rating: undefined out of 5
Keywords:
Id: IbxBRzTBeC0
Channel Id: undefined
Length: 21min 57sec (1317 seconds)
Published: Sun Apr 04 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.