SwiftUI - View Layout and Presentation

HStack

A view that arranges its children in a horizontal line.



To create static scrollable List

HStack ( alignment : . center , spacing : 20 ) {

Text ( "Hello" )

Divider ( )

Text ( "World" )

}

Documentation - HStack

LazyHStack

iOS 14

A view that arranges its children in a line that grows horizontally, creating items only as needed.

ScrollView ( . horizontal ) {

LazyHStack ( alignment : . center , spacing : 20 ) {

ForEach ( 1 . . . 100 , id : \ . self ) {

Text ( "Column \( $ 0 ) " )

}

}

}

Documentation - LazayHStack

VStack

A view that arranges its children in a vertical line.



To create static scrollable List

VStack ( alignment : . center , spacing : 20 ) {

Text ( "Hello" )

Divider ( )

Text ( "World" )

}

Documentation - VStack

LazyVStack

iOS 14

A view that arranges its children in a line that grows vertically, creating items only as needed.

ScrollView {

LazyVStack ( alignment : . leading ) {

ForEach ( 1 . . . 100 , id : \ . self ) {

Text ( "Row \( $ 0 ) " )

}

}

}

Documentation - LazyVStack

ZStack

A view that overlays its children, aligning them in both axes.

ZStack {

Text ( "Hello" )

. padding ( 10 )

. background ( Color . red )

. opacity ( 0.8 )

Text ( "World" )

. padding ( 20 )

. background ( Color . red )

. offset ( x : 0 , y : 40 )

}

}

Documentation - ZStack

List

A container that presents rows of data arranged in a single column.



To create static scrollable List

List {

Text ( "Hello world" )

Text ( "Hello world" )

Text ( "Hello world" )

}

Cell can be mixed

List {

Text ( "Hello world" )

Image ( systemName : "clock" )

}

To create dynamic List

let names = [ "John" , "Apple" , "Seed" ]

List ( names ) { name in

Text ( name )

}

To add section

List {

Section ( header : Text ( "UIKit" ) , footer : Text ( "We will miss you" ) ) {

Text ( "UITableView" )

}



Section ( header : Text ( "SwiftUI" ) , footer : Text ( "A lot to learn" ) ) {

Text ( "List" )

}

}

To make it grouped add .listStyle(GroupedListStyle())

List {

Section ( header : Text ( "UIKit" ) , footer : Text ( "We will miss you" ) ) {

Text ( "UITableView" )

}



Section ( header : Text ( "SwiftUI" ) , footer : Text ( "A lot to learn" ) ) {

Text ( "List" )

}

} . listStyle ( GroupedListStyle ( ) )

To make it inset grouped ( .insetGrouped ), add .listStyle(GroupedListStyle()) and force regular horizontal size class .environment(\.horizontalSizeClass, .regular) .

List {

Section ( header : Text ( "UIKit" ) , footer : Text ( "We will miss you" ) ) {

Text ( "UITableView" )

}



Section ( header : Text ( "SwiftUI" ) , footer : Text ( "A lot to learn" ) ) {

Text ( "List" )

}

} . listStyle ( GroupedListStyle ( ) )

. environment ( \ . horizontalSizeClass , . regular )

Inset grouped was added to SwiftUI in iOS 13.2

iOS 14

In iOS 14, we have a dedicated style for this.

. listStyle ( InsetGroupedListStyle ( ) )

Documentation - List

ScrollView

scroll view.

ScrollView ( alwaysBounceVertical : true ) {

Image ( "foo" )

Text ( "Hello World" )

}

Documentation - ScrollView

LazyHGrid

iOS 14

A container view that arranges its child views in a grid that grows horizontally, creating items only as needed.

var rows : [ GridItem ] =

Array ( repeating : . init ( . fixed ( 20 ) ) , count : 2 )



ScrollView ( . horizontal ) {

LazyHGrid ( rows : rows , alignment : . top ) {

ForEach ( ( 0 . . . 100 ) , id : \ . self ) {

Text ( " \( $ 0 ) " ) . background ( Color . pink )

}

}

}

Documentation - LazyHGrid

LazyVGrid

iOS 14

A container view that arranges its child views in a grid that grows vertically, creating items only as needed.

var columns : [ GridItem ] =

Array ( repeating : . init ( . fixed ( 20 ) ) , count : 5 )



ScrollView {

LazyVGrid ( columns : columns ) {

ForEach ( ( 0 . . . 100 ) , id : \ . self ) {

Text ( " \( $ 0 ) " ) . background ( Color . pink )

}

}

}

Documentation - LazyVGrid

Form

A container for grouping controls used for data entry, such as in settings or inspectors.



You can put almost anything into this Form and it will render appropriate style for a form.

NavigationView {

Form {

Section {

Text ( "Plain Text" )

Stepper ( value : $quantity , in : 0 . . . 10 , label : { Text ( "Quantity" ) } )

}

Section {

DatePicker ( $date , label : { Text ( "Due Date" ) } )

Picker ( selection : $selection , label :

Text ( "Picker Name" )

, content : {

Text ( "Value 1" ) . tag ( 0 )

Text ( "Value 2" ) . tag ( 1 )

Text ( "Value 3" ) . tag ( 2 )

Text ( "Value 4" ) . tag ( 3 )

} )

}

}

}

Documentation - Form

Spacer

A flexible space that expands along the major axis of its containing stack layout, or on both axes if not contained in a stack.

HStack {

Image ( systemName : "clock" )

Spacer ( )

Text ( "Time" )

}

Documentation - Spacer

Divider

A visual element that can be used to separate other content.

HStack {

Image ( systemName : "clock" )

Divider ( )

Text ( "Time" )

} . fixedSize ( )

Documentation - Divider

NavigationView

A view for presenting a stack of views representing a visible path in a navigation hierarchy.

NavigationView {

List {

Text ( "Hello World" )

}

. navigationBarTitle ( Text ( "Navigation Title" ) )

}

For old style title

NavigationView {

List {

Text ( "Hello World" )

}

. navigationBarTitle ( Text ( "Navigation Title" ) , displayMode : . inline )

}

Add UIBarButtonItem

NavigationView {

List {

Text ( "Hello World" )

}

. navigationBarItems ( trailing :

Button ( action : {



} , label : {

Text ( "Add" )

} )

)

. navigationBarTitle ( Text ( "Navigation Title" ) )

}

Add show / push with NavigationLink



Use as UISplitViewController .

NavigationView {

List {

NavigationLink ( "Go to detail" , destination : Text ( "New Detail" ) )

} . navigationBarTitle ( "Master" )

Text ( "Placeholder for Detail" )

}

You can style a NavigationView using two new style properties: stack and doubleColumn . By default, navigation views on iPhone and Apple TV visually reflect a navigation stack, while on iPad and Mac, a split-view styled navigation view displays.

You can override this with .navigationViewStyle .

NavigationView {

MyMasterView ( )

MyDetailView ( )

}

. navigationViewStyle ( StackNavigationViewStyle ( ) )

iOS 14

In iOS 14, there is a new sidebar style for UISplitViewController . You can also do that by putting three views under NavigationView .

NavigationView {

Text ( "Sidebar" )

Text ( "Primary" )

Text ( "Detail" )

}

Documentation - NavigationView

iOS 14

Add UIToolbar like toolbarItems in UIViewController .

NavigationView {

Text ( "SwiftUI" ) . padding ( )

. toolbar {

ToolbarItem ( placement : . bottomBar ) {

Button {



} label : {

Image ( systemName : "archivebox" )

}

}



ToolbarItem ( placement : . bottomBar ) {

Spacer ( )

}



ToolbarItem ( placement : . bottomBar ) {

Button {



} label : {

Image ( systemName : "square.and.pencil" )

}

}

}

}

Documentation - ToolbarItem

TabView

A view that allows for switching between multiple child views using interactable user interface elements.

TabView {

Text ( "First View" )

. font ( . title )

. tabItem ( { Text ( "First" ) } )

. tag ( 0 )

Text ( "Second View" )

. font ( . title )

. tabItem ( { Text ( "Second" ) } )

. tag ( 1 )

}

Image and Text together. You can use SF Symbol here.

TabView {

Text ( "First View" )

. font ( . title )

. tabItem ( {

Image ( systemName : "circle" )

Text ( "First" )

} )

. tag ( 0 )

Text ( "Second View" )

. font ( . title )

. tabItem ( VStack {

Image ( "second" )

Text ( "Second" )

} )

. tag ( 1 )

}

Or you can omit VStack

TabView {

Text ( "First View" )

. font ( . title )

. tabItem ( {

Image ( systemName : "circle" )

Text ( "First" )

} )

. tag ( 0 )

Text ( "Second View" )

. font ( . title )

. tabItem ( {

Image ( "second" )

Text ( "Second" )

} )

. tag ( 1 )

}

UIPageViewController

UIPageViewController become a style of TabView . To use page view style, use .tabViewStyle(PageTabViewStyle()) modifier on your tab view.

TabView {

Text ( "1" )

. frame ( maxWidth : . infinity , maxHeight : . infinity )

. background ( Color . pink )

Text ( "2" )

. frame ( maxWidth : . infinity , maxHeight : . infinity )

. background ( Color . red )

Text ( "3" )

. frame ( maxWidth : . infinity , maxHeight : . infinity )

. background ( Color . green )

Text ( "4" )

. frame ( maxWidth : . infinity , maxHeight : . infinity )

. background ( Color . blue )

} . tabViewStyle ( PageTabViewStyle ( ) )

This PageTabViewStyle will include UIPageControl at the bottom just like UIPageViewController . To remove it, pass indexDisplayMode to PageTabViewStyle .

. tabViewStyle ( PageTabViewStyle ( indexDisplayMode : . never ) )

There is a new style for page control which will render background around the indicator. To enforce this new style, add .indexViewStyle(PageIndexViewStyle(backgroundDisplayMode: .always)) modifier to your tab view.

TabView {

Text ( "1" )

. frame ( maxWidth : . infinity , maxHeight : . infinity )

. background ( Color . pink )

Text ( "2" )

. frame ( maxWidth : . infinity , maxHeight : . infinity )

. background ( Color . red )

Text ( "3" )

. frame ( maxWidth : . infinity , maxHeight : . infinity )

. background ( Color . green )

Text ( "4" )

. frame ( maxWidth : . infinity , maxHeight : . infinity )

. background ( Color . blue )

}

. indexViewStyle ( PageIndexViewStyle ( backgroundDisplayMode : . always ) )

. tabViewStyle ( PageTabViewStyle ( ) )

Documentation - TabView

Alert

A container for an alert presentation.



We can show Alert based on boolean.

@ State var isError : Bool = false



Button ( "Alert" ) {

self . isError = true

} . alert ( isPresented : $isError , content : {

Alert ( title : Text ( "Error" ) , message : Text ( "Error Reason" ) , dismissButton : . default ( Text ( "OK" ) ) )

} )

It is also bindable with Identifiable item.

@ State var error : AlertError ?



var body : some View {

Button ( "Alert Error" ) {

self . error = AlertError ( reason : "Reason" )

} . alert ( item : $error , content : { error in

alert ( reason : error . reason )

} )

}



func alert ( reason : String ) - > Alert {

Alert ( title : Text ( "Error" ) ,

message : Text ( reason ) ,

dismissButton : . default ( Text ( "OK" ) )

)

}



struct AlertError : Identifiable {

var id : String {

return reason

}



let reason : String

}

Documentation - Alert

Modal

A modal transition.



We can show Modal based on boolean.

@ State var isModal : Bool = false



var modal : some View {

Text ( "Modal" )

}



Button ( "Modal" ) {

self . isModal = true

} . sheet ( isPresented : $isModal , content : {

self . modal

} )

Documentation - Sheet

It is also bindable with Identifiable item.

@ State var detail : ModalDetail ?



var body : some View {

Button ( "Modal" ) {

self . detail = ModalDetail ( body : "Detail" )

} . sheet ( item : $detail , content : { detail in

self . modal ( detail : detail . body )

} )

}



func modal ( detail : String ) - > some View {

Text ( detail )

}



struct ModalDetail : Identifiable {

var id : String {

return body

}



let body : String

}

Documentation - Sheet

ActionSheet

A storage type for an action sheet presentation.



We can show ActionSheet based on boolean.

@ State var isSheet : Bool = false



var actionSheet : ActionSheet {

ActionSheet ( title : Text ( "Action" ) ,

message : Text ( "Description" ) ,

buttons : [

. default ( Text ( "OK" ) , action : {



} ) ,

. destructive ( Text ( "Delete" ) , action : {



} )

]

)

}



Button ( "Action Sheet" ) {

self . isSheet = true

} . actionSheet ( isPresented : $isSheet , content : {

self . actionSheet

} )

It is also bindable with Identifiable item.

@ State var sheetDetail : SheetDetail ?



var body : some View {

Button ( "Action Sheet" ) {

self . sheetDetail = ModSheetDetail ( body : "Detail" )

} . actionSheet ( item : $sheetDetail , content : { detail in

self . sheet ( detail : detail . body )

} )

}



func sheet ( detail : String ) - > ActionSheet {

ActionSheet ( title : Text ( "Action" ) ,

message : Text ( detail ) ,

buttons : [

. default ( Text ( "OK" ) , action : {



} ) ,

. destructive ( Text ( "Delete" ) , action : {



} )

]

)

}



struct SheetDetail : Identifiable {

var id : String {

return body

}



let body : String

}

Documentation - ActionSheet