{"componentChunkName":"component---src-templates-article-js","path":"/article/weatherui-in-swiftui","result":{"data":{"contentfulArticle":{"title":"Weather App Design","image":"https://res.cloudinary.com/liquidcoder/image/upload/v1569692184/swiftui-weatherapp/shmhqyicqn4dlxbu4s2v.jpg","createdDate":"Saturday, 05 October 2019","videoUrl":"n8XwWWW5TtU","updatedDate":"3 years ago","isNewLayout":null,"previews":null,"slug":"weatherui-in-swiftui","author":{"email":"john@liquidcoder.com","name":"John"},"tags":[{"title":"Swift UI"},{"title":"Article"},{"title":"UI Design"},{"title":"Free"}],"allChapters":null,"code":null,"explanation":null,"downloads":[{"name":"Project source code","url":"https://github.com/liquidcoder1/weatherUI/archive/master.zip"}],"resources":[{"name":"Apple developer documentation","url":"https://developer.apple.com/documentation/swiftui"},{"name":"Hacking with swift","url":"https://www.hackingwithswift.com"},{"name":"Swift by sundell","url":"https://www.swiftbysundell.com"}],"body":{"childMdx":{"tableOfContents":{"items":[{"url":"#introduction","title":"Introduction"},{"url":"#requirements","title":"Requirements"},{"url":"#get-started","title":"Get started"},{"url":"#custom-navigation-bar","title":"Custom Navigation Bar"},{"url":"#main-card-view","title":"Main Card View"},{"url":"#horizontal-scrolling-list-of-cards-next-7-days","title":"Horizontal scrolling list of cards: Next 7 days"},{"url":"#details-view","title":"Details View"},{"url":"#close-button","title":"Close Button"},{"url":"#drag-dismissal","title":"Drag dismissal"}]},"body":"function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\n\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n\n/* @jsxRuntime classic */\n\n/* @jsx mdx */\nvar _frontmatter = {};\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n      props = _objectWithoutProperties(_ref, [\"components\"]);\n\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"h2\", null, \"Introduction\"), mdx(\"p\", null, \"Normally, I would start by telling you what swift UI is and blessed blah blah blah\\u2026 However , I am not going to do that. If you are here, chances are you know already what Swift UI is.So, let\\u2019s just jump right into it, shall we?\"), mdx(\"h2\", null, \"Requirements\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Xcode 12 or greater\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"MacOS Catalina or greater\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Swift 5.0 or greater\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"iOS 14 or greater\")), mdx(\"h2\", null, \"Get started\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Open Xcode\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Create a new Xcode project\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Select single view app and click next\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Name your app and make sure the user interface is Swift UI\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Last, click Finish.\\nYour newly created project should  have the following files:\")), mdx(\"p\", null, mdx(\"img\", _extends({\n    parentName: \"p\"\n  }, {\n    \"src\": \"https://res.cloudinary.com/liquidcoder/image/upload/v1569169473/swiftui-weatherapp/Screenshot_2019-09-22_at_17.09.19_mw8xyv.png\",\n    \"alt\": null,\n    \"title\": \"Swift UI - Project layout\"\n  })), mdx(\"img\", _extends({\n    parentName: \"p\"\n  }, {\n    \"src\": \"https://res.cloudinary.com/liquidcoder/image/upload/v1569169667/swiftui-weatherapp/Screenshot_2019-09-22_at_17.10.26_ye0t0r.png\",\n    \"alt\": \"Swift UI - Initial code\"\n  })), mdx(\"img\", _extends({\n    parentName: \"p\"\n  }, {\n    \"src\": \"https://res.cloudinary.com/liquidcoder/image/upload/v1569169667/swiftui-weatherapp/swiftui-preview_yxnv1j.png\",\n    \"alt\": \"SwiftUI - Similator preview\"\n  }))), mdx(\"p\", null, \"That the default project layout when you first create the project. If the simulator does not show up, click resume.\"), mdx(\"p\", null, \"Rename the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ContentView\"), \" filename and struct to WeatherApp and make sure you rename its reference in \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"SceneDelegate\")), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \"func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {\\n    // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.\\n    // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.\\n    // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).\\n\\n    // Create the SwiftUI view that provides the window contents.\\n :: let weatherApp = WeatherApp()::\\n\\n    // Use a UIHostingController as window root view controller.\\n    if let windowScene = scene as? UIWindowScene {\\n        let window = UIWindow(windowScene: windowScene)\\n        window.rootViewController = ::UIHostingController(rootView: weatherApp)::\\n        self.window = window\\n        window.makeKeyAndVisible()\\n    }\\n}\\n\")), mdx(\"p\", null, \"Change everywhere you see \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ContentView\"), \" referenced to WeatherApp.\"), mdx(\"p\", null, \"Every time you make a big change, the preview will disappear, just click resume to make it appear again.\"), mdx(\"p\", null, \"I will create the UI step by step, that will be easier to understand and for to explain. Here is how to I am going to do that:\"), mdx(\"p\", null, mdx(\"img\", _extends({\n    parentName: \"p\"\n  }, {\n    \"src\": \"https://res.cloudinary.com/liquidcoder/image/upload/v1569171771/swiftui-weatherapp/swiftui-3_pdmqbz.png\",\n    \"alt\": \"Swift UI - Step 1 - 3\"\n  })), mdx(\"img\", _extends({\n    parentName: \"p\"\n  }, {\n    \"src\": \"https://res.cloudinary.com/liquidcoder/image/upload/v1569171771/swiftui-weatherapp/swiftui-2_egax5x.png\",\n    \"alt\": \"swift UI - Step 4\"\n  }))), mdx(\"h2\", null, \"Custom Navigation Bar\"), mdx(\"p\", null, \"Replace \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Text(\\u201C\\u201DHello World)\"), \" inside \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"body\"), \" with \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"\\xA0NavBarView(country: \\\"Bahamas\\u201D)\"), \", then create a new \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"View\"), \"  named NavBarView outside the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"WeatherApp\"), \" struct block above \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ContentView_Previews\"), \" block. The NavBarView should look like this:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \"struct NavBarView: View {\\n    \\n    var country = \\\"France\\\"\\n    \\n    var body: some View{\\n        HStack {\\n            Image(systemName: \\\"ellipsis.circle.fill\\\")\\n                .resizable()\\n                .frame(width: 25, height: 25)\\n            Spacer()\\n            Text(country).font(.title)\\n            Spacer()\\n            Image(systemName: \\\"magnifyingglass\\\")\\n                .resizable()\\n                .frame(width: 25, height: 25)\\n            \\n        }.padding()\\n    }\\n}\\n\\n\")), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Explanation:\"), \"\\nI create an image with system icons which you can find by downloading the SF Symbol from apple developer website. I set the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"resizable()\"), \" modifier  in order to give the images a different frame which I set right after \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"resible()\"), \".\"), mdx(\"p\", null, \"I put the title \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Text\"), \" between 2 \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Spacer\"), \" views to make it  centered horizontally. \"), mdx(\"p\", null, \"I add a padding on all side of the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"HStack\"), \"View which is the container that lays out the 5 views horizontally.\"), mdx(\"p\", null, \"Add the following below the NavBarView call in \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"WeatherApp\")), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \"  Picker(\\\"\\\", selection: $selected){\\n           Text(\\\"Today\\\").tag(0)\\n           Text(\\\"Tomorrow\\\").tag(1)\\n       }.pickerStyle(SegmentedPickerStyle() )\\n           .padding(.horizontal)\\n\")), mdx(\"p\", null, \"You will get an errors. To fix them, surround everything with a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"VStack\"), \" block and   add this    \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"@State private var selected = 0\"), \" above \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"body\")), mdx(\"p\", null, \"Your WeatherApp struct should now look like this:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \"\\nstruct WeatherApp: View {\\n     @State private var selected = 0\\n    \\n    var body: some View {\\n        \\n        VStack {\\n            NavBarView(country: \\\"Bahamas\\\")\\n            \\n            Picker(\\\"\\\", selection: $selected){\\n                     Text(\\\"Today\\\").tag(0)\\n                     Text(\\\"Tomorrow\\\").tag(1)\\n                 }.pickerStyle(SegmentedPickerStyle() )\\n                     .padding(.horizontal)\\n        }\\n    }\\n}\\n\")), mdx(\"p\", null, \"The preview should now show this (at the centre of the screen):\\n\", mdx(\"img\", _extends({\n    parentName: \"p\"\n  }, {\n    \"src\": \"https://res.cloudinary.com/liquidcoder/image/upload/v1569175729/swiftui-weatherapp/swiftui_navbar_bjff4v.png\",\n    \"alt\": \"SwiftUI - Custom Navvigation Bar\"\n  }))), mdx(\"h2\", null, \"Main Card View\"), mdx(\"p\", null, \"We will need to create 2 files for this step, the first one is a modal file that will hold our dummy data and the other one will be the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"MainCardView\"), \".\"), mdx(\"p\", null, \"Create the first file, name it \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Weather.swift\"), \" and add the following code in it:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \"import Foundation\\n\\n\\nstruct Weather: Hashable, Identifiable {\\n    let id: Int\\n    let day: String\\n    let weatherIcon: String\\n    let currentTemp: String\\n    let minTemp: String\\n    let maxTemp: String\\n    let color: String\\n    \\n    static var sampleData: [Weather] {\\n        return [\\n            Weather(id: 1, day: \\\"Monday\\\", weatherIcon: \\\"sun.max\\\", currentTemp: \\\"50\\\", minTemp: \\\"52\\\", maxTemp: \\\"69\\\", color: \\\"mainCard\\\"),\\n            Weather(id: 2, day: \\\"Tuesday\\\", weatherIcon: \\\"sun.dust\\\", currentTemp: \\\"33\\\", minTemp: \\\"52\\\", maxTemp: \\\"69\\\", color: \\\"tuesday\\\"),\\n            Weather(id: 3, day: \\\"Wednesday\\\", weatherIcon: \\\"cloud.sun.rain\\\", currentTemp: \\\"38\\\", minTemp: \\\"52\\\", maxTemp: \\\"59\\\", color: \\\"wednesday\\\"),\\n            Weather(id: 4, day: \\\"Thursday\\\", weatherIcon: \\\"cloud.sun.bolt\\\", currentTemp: \\\"33\\\", minTemp: \\\"52\\\", maxTemp: \\\"60\\\", color: \\\"thursday\\\"),\\n            Weather(id: 5, day: \\\"Friday\\\", weatherIcon: \\\"sun.haze\\\", currentTemp: \\\"40\\\", minTemp: \\\"52\\\", maxTemp: \\\"69\\\", color: \\\"friday\\\"),\\n            Weather(id: 6, day: \\\"Saturday\\\", weatherIcon: \\\"sun.dust\\\", currentTemp: \\\"50\\\", minTemp: \\\"52\\\", maxTemp: \\\"38\\\", color: \\\"saturday\\\"),\\n            Weather(id: 7, day: \\\"Sunday\\\", weatherIcon: \\\"sun.max\\\", currentTemp: \\\"50\\\", minTemp: \\\"52\\\", maxTemp: \\\"69\\\", color: \\\"sunday\\\")\\n        ]\\n    }\\n}\\n\\n\\n\")), mdx(\"p\", null, \"Then create a SwiftUI View file, name it \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"MainCardView.swift\"), \" . Click resume if the preview doesn\\u2019t show up.\"), mdx(\"p\", null, \"Replace this:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \" var body: some View {\\n        Text(\\\"Hello World!\\u201D)\\n }\\n\")), mdx(\"p\", null, \"with the following:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \"    @Binding var weather: Weather\\n    \\n    var body: some View{\\n        ZStack {\\n            \\n            Image(\\\"card-bg\\\")\\n                .resizable().aspectRatio(contentMode: .fill)\\n            \\n            VStack(spacing: 10){\\n                Text(\\\"\\\\(weather.currentTemp)\\xB0\\\")\\n                    .foregroundColor(Color.white)\\n                    .fontWeight(Font.Weight.heavy)\\n                    .font(Font.system(size: 70))\\n                \\n                Image(systemName: weather.weatherIcon)\\n                .resizable()\\n                .foregroundColor(Color.white)\\n                .frame(width: 100, height: 100)\\n                .aspectRatio(contentMode: .fit)\\n                    \\n                    \\n                \\n                Text(\\\"\\\\(weather.maxTemp)\\xB0\\\")\\n                    .foregroundColor(Color.white)\\n                    .font(.title)\\n                    .padding(.vertical)\\n            }\\n        }\\n        .frame(minWidth: 0 , maxWidth: .infinity)\\n        .background(Color(weather.color))\\n    }\\n\")), mdx(\"p\", null, \"You will get the following error:\"), mdx(\"p\", null, mdx(\"img\", _extends({\n    parentName: \"p\"\n  }, {\n    \"src\": \"https://res.cloudinary.com/liquidcoder/image/upload/v1569181509/swiftui-weatherapp/SwiftUI_Parameter_error_m5nhxr.png\",\n    \"alt\": \"Swift UI Parameter error\"\n  }))), mdx(\"p\", null, \"Replace the entire block with this:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \"\\nstruct MainCardView_Previews: PreviewProvider {\\n    static var previews: some View {\\n        MainCardView(weather: .constant(Weather.sampleData[0]))\\n    }\\n}\\n\\n\")), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Explanation:\"), \"\\nThe code is pretty self explanatory, but I will go over what I did in case there is confusion.\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"I added \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"@Binding\"), \" before the weather property and with that swiftUI automagically watch for changes to that property and update the part of the UI that uses. \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"@Binding\"), \" works the same way as \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"@State\"), \" does but instead of being local, it\\u2019s global.\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"I make the image resizable and then set its content mode to fill to stretch it out\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"stack the 2 text views and the weatherIcon vertically.\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"I set the frame\\u2019s minWidth and maxWidth to 0 and .infinity to make the width match the screen width on all device sizes.\"))), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, mdx(\"em\", {\n    parentName: \"p\"\n  }, \"Tip:  Use \", mdx(\"inlineCode\", {\n    parentName: \"em\"\n  }, \"\\xA0.frame(minWidth: 0 , maxWidth: .infinity)\"), \" or  \", mdx(\"inlineCode\", {\n    parentName: \"em\"\n  }, \".frame(minHeight: 0 , maxHeight: .infinity)\"), \" to fill the parent\\u2019s width or height no matter the screen size (the view you set the frame to will be responsive).\"))), mdx(\"p\", null, \"The preview should now show this: \"), mdx(\"p\", null, \"\\xA0\", mdx(\"img\", _extends({\n    parentName: \"p\"\n  }, {\n    \"src\": \"https://res.cloudinary.com/liquidcoder/image/upload/v1569269521/swiftui-weatherapp/swiftuI-5_iygwwf.png\",\n    \"alt\": \"SWIFT ui\"\n  }))), mdx(\"p\", null, \"The next thing to do is to add our newly created view to the WeatherApp View.\\nAdd this below the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"selected\"), \"  property:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \" @State private var weather =  Weather(id: 1, day: \\\"Monday\\\", weatherIcon: \\\"sun.max\\\", currentTemp: \\\"50\\\", minTemp:\\\"52\\\", maxTemp: \\\"69\\\", color: \\\"mainCard\\\")\\n\")), mdx(\"p\", null, \"And this below the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Picker\"), \"  call:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \"  MainCardView(weather: $weather)\\n                .cornerRadius(CGFloat(20))\\n                .padding()\\n                .shadow(color: Color(self.weather.color)\\n                .opacity(0.4), radius: 20, x: 0, y: 20)\\n\")), mdx(\"p\", null, mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"$\"), \" (dollar sign) before weather means that the state of this local weather property is bound to the property located in MainCardView, whenever one of Them changes, the other will be notified and change accordingly. Simply put, they are in sync.\\nThe rest of the code is self explanatory.\\nAfter making those changes, click resume and the preview should show this:\"), mdx(\"p\", null, mdx(\"img\", _extends({\n    parentName: \"p\"\n  }, {\n    \"src\": \"https://res.cloudinary.com/liquidcoder/image/upload/v1569270213/swiftui-weatherapp/SwiftUI_Card_hbkekm.png\",\n    \"alt\": \"SwiftUI Card\"\n  }))), mdx(\"h2\", null, \"Horizontal scrolling list of cards: Next 7 days\"), mdx(\"p\", null, \"Let\\u2019s first create the title. Add the following code below \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"MainCardView\"), \":\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \"Text(\\\"Next 7 days\\\").foregroundColor(Color(\\\"text\\\"))\\n         .font(.system(size: 22))\\n         .fontWeight(.bold)\\n         .frame(minWidth:0, maxWidth: .infinity, alignment: .leading)\\n         .padding(.horizontal)\\n\")), mdx(\"p\", null, \"The above code just create a text, align it to the left and apply an horizontal padding.\"), mdx(\"p\", null, \"The next step is to create a single card that will be reused to create the horizontally scrolling list. \"), mdx(\"p\", null, \"Create a new SwiftUI file, the same way you did for the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"MainCardView\"), \", name it \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"SmallCard\"), \"for lack of a better word.\"), mdx(\"p\", null, \"Replace the content of that file with the Following:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \"import SwiftUI\\n\\nstruct SmallCard: View {\\n    var weather: Weather = Weather(id: 1, day: \\\"Monday\\\", weatherIcon:  \\\"sun.max\\\", currentTemp:  \\\"40\\\", minTemp: \\\"25\\\", maxTemp: \\\"69\\\", color: \\\"mainCard\\\")\\n       \\n       var body: some View{\\n           \\n           VStack(spacing: 20) {\\n               // Day text\\n               Text(self.weather.day).fontWeight(.bold)\\n                   .foregroundColor(Color.white)\\n               \\n               // Weather icon\\n               Image(systemName: self.weather.weatherIcon)\\n                   .resizable()\\n                   .foregroundColor(Color.white)\\n                   .frame(width: 60, height: 60)\\n               \\n               \\n               // Temp texts\\n               ZStack {\\n                   Image(\\\"cloud\\\")\\n                       .resizable()\\n                       .scaledToFill()\\n                       .offset(CGSize(width: 0, height: 30))\\n                   \\n                   VStack(spacing: 8) {\\n                       Text(\\\"\\\\(self.weather.currentTemp)\\xB0\\\").font(.title).foregroundColor(Color.white).fontWeight(.bold)\\n                       HStack {\\n                           Text(\\\"\\\\(self.weather.minTemp)\\xB0\\\").foregroundColor(Color(\\\"light-text\\\"))\\n                           Text(\\\"\\\\(self.weather.maxTemp)\\xB0\\\").foregroundColor(Color.white)\\n                       }\\n                   }\\n               }\\n           }\\n           .frame(width: 200, height: 300)\\n           .background(Color(self.weather.color))\\n           .cornerRadius(30)\\n           .shadow(color: Color(weather.color).opacity(0.7), radius: 10, x: 0, y: 8)\\n       }\\n}\\n\\nstruct SmallCard_Previews: PreviewProvider {\\n    static var previews: some View {\\n        SmallCard( )\\n    }\\n}\\n\\n\")), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Explanation:\"), \"\\nThe code is very familiar with what I have previously. The only new thing is  the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ZStack\"), \" container. A \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ZStack\"), \" is a view that overlays its child views on top of each other. (\\u201CZ\\u201D represents the Z-axis which is depth-based in a 3D space)\"), mdx(\"p\", null, \"We push the cloud icon down by 30 by setting its height offset to 30.\"), mdx(\"p\", null, \"You should take your time reading the code line by line as it\\u2019s pretty self-explanatory.\"), mdx(\"p\", null, \"The previews should now display this:\"), mdx(\"p\", null, mdx(\"img\", _extends({\n    parentName: \"p\"\n  }, {\n    \"src\": \"https://res.cloudinary.com/liquidcoder/image/upload/v1569354499/swiftui-weatherapp/mqb3ei8svldklzc2ec0c.png\",\n    \"alt\": \"Swift UI - Small card\"\n  }))), mdx(\"p\", null, \"Let\\u2019s now create  the horizontally scrolling list. Go back the WeatherApp file.\"), mdx(\"p\", null, \"Add the following code below the next 7 days text:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \"  ScrollView(.horizontal, showsIndicators: false) {\\n                HStack(spacing: 20) {\\n                    ForEach(Weather.sampleData, id: \\\\.id) { weather                             in\\n                        SmallCard(weather: weather)\\n                    }\\n                }.frame( height: 380)\\n                    .padding(.horizontal)\\n            }.frame( height: 350, alignment: .top)\\n\")), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Explanation:\"), \"\\nIn the above code, I put an horizontal stacked list of  \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"SmallCard\"), \"  (that we\\u2019ve just created earlier) inside a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ScrollView\"), \" to make it scrollable.\"), mdx(\"p\", null, mdx(\"em\", {\n    parentName: \"p\"\n  }, mdx(\"strong\", {\n    parentName: \"em\"\n  }, \"Note\"), \": This is not the most efficient way to create a list of items, use it when you know the number of items is small and limited. Use \", mdx(\"a\", _extends({\n    parentName: \"em\"\n  }, {\n    \"href\": \"https://developer.apple.com/documentation/swiftui/list\",\n    \"title\": \"Swift UI List\"\n  }), \"List\"), \" for  a big list of items\")), mdx(\"p\", null, \"After that the preview should show this:\"), mdx(\"p\", null, mdx(\"img\", _extends({\n    parentName: \"p\"\n  }, {\n    \"src\": \"https://res.cloudinary.com/liquidcoder/image/upload/v1569392476/swiftui-weatherapp/d1g8o9ldwntqrcsnqsic.png\",\n    \"alt\": \"SwiftUI Screen\"\n  }))), mdx(\"h2\", null, \"Details View\"), mdx(\"p\", null, \"When one of the  small cards is clicked, we want to show the detail view which will animate from the bottom.\"), mdx(\"p\", null, \"Go ahead and create a new swiftUI file named \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"DetailView.swift\"), \"\\nReplace the content of the file with this:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \"import SwiftUI\\n\\nstruct DetailView: View {\\n    \\n    @Binding var weather: Weather\\n    \\n    var body: some View {\\n        GeometryReader { gr in\\n            VStack(spacing: 20) {\\n                \\n                // Day text\\n                Text(self.weather.day).fontWeight(.bold)\\n                    .font(.system(size: 60))\\n                    .frame(height: gr.size.height * 1/10)\\n                    .minimumScaleFactor(0.5)\\n                    .foregroundColor(Color.white)\\n                \\n                // Weather image\\n                Image(systemName: self.weather.weatherIcon)\\n                    .resizable()\\n                    .foregroundColor(Color.white)\\n                    .frame(width: gr.size.height * 3 / 10, height: gr.size.height * 3 / 10)\\n                \\n                // Degrees texts\\n                VStack {\\n                    VStack(spacing: 20) {\\n                        Text(\\\"\\\\(self.weather.currentTemp)\\xB0\\\")\\n                            .font(.system(size: 50))\\n                            .foregroundColor(Color.white)\\n                            .fontWeight(.bold)\\n                            .frame(height: gr.size.height * 0.7/10)\\n                            .minimumScaleFactor(0.5)\\n                        \\n                        HStack(spacing: 40) {\\n                            Text(\\\"\\\\(self.weather.minTemp)\\xB0\\\")\\n                                .foregroundColor(Color(\\\"light-text\\\"))\\n                                .font(.title)\\n                                .minimumScaleFactor(0.5)\\n                            \\n                            Text(\\\"\\\\(self.weather.maxTemp)\\xB0\\\")\\n                                .foregroundColor(Color.white)\\n                                .font(.title)\\n                                .minimumScaleFactor(0.5)\\n                        }\\n                    }\\n                }\\n                }.frame(minWidth: 0, maxWidth: .infinity, minHeight: gr.size.height, alignment: .bottom)\\n            .background(Color(self.weather.color))\\n        }\\n    }\\n}\\n\\nstruct DetailView_Previews: PreviewProvider {\\n    static var previews: some View {\\n        DetailView(weather: .constant(Weather(id: 1, day: \\\"Monday\\\", weatherIcon:  \\\"sun.max\\\", currentTemp:  \\\"40\\\", minTemp: \\\"25\\\", maxTemp: \\\"69\\\", color: \\\"mainCard\\\")))\\n    }\\n}\\n\\n\")), mdx(\"p\", null, \"The code is pretty similar with what we did earlier except I surrounded the outer \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"VStack\"), \" with \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"GeometryReader\"), \".\"), mdx(\"p\", null, mdx(\"em\", {\n    parentName: \"p\"\n  }, mdx(\"strong\", {\n    parentName: \"em\"\n  }, \"Note\"), \": \", mdx(\"inlineCode\", {\n    parentName: \"em\"\n  }, \"GeometryReader\"), \" is a container view that defines its content as a function of its own size and coordinate space. What that means is  that\", mdx(\"inlineCode\", {\n    parentName: \"em\"\n  }, \"GeometryReader\"), \"  gives us the size and position that we can use to dynamically position and resize child views\")), mdx(\"p\", null, \"The preview should show this:\"), mdx(\"p\", null, mdx(\"img\", _extends({\n    parentName: \"p\"\n  }, {\n    \"src\": \"https://res.cloudinary.com/liquidcoder/image/upload/v1569443647/swiftui-weatherapp/n5iu9ooqx46yvrvqn2w6.png\",\n    \"alt\": \"SwiftUI - Details\"\n  }))), mdx(\"p\", null, \"The next step is to make the view rounded only at the top left and right. To do that we will create a custom shape that we will use to clip the view. Let\\u2019s go.\"), mdx(\"p\", null, \"Add this below \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"DetailsView\"), \" and outside its block:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \"struct CustomShape: Shape {\\n    func path(in rect: CGRect) -> Path {\\n        let cornerRadius:CGFloat = 40\\n        var path = Path()\\n        \\n        path.move(to:  CGPoint(x: 0, y: cornerRadius))\\n        path.addQuadCurve(to: CGPoint(x: cornerRadius, y: 0), control: CGPoint.zero)\\n        path.addLine(to: CGPoint(x: rect.width - cornerRadius, y: 0))\\n        path.addQuadCurve(to: CGPoint(x: rect.width, y: cornerRadius), control: CGPoint(x: rect.width , y: 0))\\n        path.addLine(to: CGPoint(x: rect.width, y: rect.height))\\n        path.addLine(to: CGPoint(x: 0, y: rect.height))\\n        path.closeSubpath()\\n\\n        return path\\n    }\\n}\\n\")), mdx(\"p\", null, \"And add this modifier on the parent \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"VStack\"), \" in \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"DetailsView\"), \" after  \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".background(Color(self.weather.color))\"), \":\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \"  .clipShape(CustomShape(), style: FillStyle.init(eoFill: true, antialiased: true))\\n\")), mdx(\"p\", null, \"You should see this on the preview:\\n\", mdx(\"img\", _extends({\n    parentName: \"p\"\n  }, {\n    \"src\": \"https://res.cloudinary.com/liquidcoder/image/upload/v1569478863/swiftui-weatherapp/to5zazzha4upyqn9cdpk.png\",\n    \"alt\": \"SwiftUI - Clip view\"\n  }))), mdx(\"p\", null, \"Next, we will create the next 5 hours view:\\nAdd this code below the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"CustomShape\"), \" block:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \"struct HourView: View {\\n    var hour = \\\"14:00\\\"\\n    var icon = \\\"sun.max.fill\\\"\\n    var color = \\\"wednesday\\\"\\n    \\n    var body: some View {\\n        GeometryReader { gr in\\n            VStack{\\n                Text(self.hour).foregroundColor(Color(\\\"text\\\"))\\n                Image(systemName: self.icon)\\n                    .resizable()\\n                    .foregroundColor(Color(self.color))\\n                    .frame(width: gr.size.height * 1/3, height: gr.size.height * 1/3)\\n                \\n                Text(\\\"24\\xB0\\\")\\n                    .font(.system(size: 24))\\n                    .foregroundColor(Color(\\\"text\\\"))\\n                    .fontWeight(.semibold)\\n            }\\n        }.padding(.vertical, 30)\\n    }\\n}\\n\\n\")), mdx(\"p\", null, \"And create the horizontal stacked view like this:\\nPut the following code below, VStack in \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"DetailView\"), \"\\u2019s body.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {}), \"     // Hourly views\\n                HStack (spacing: 20){\\n                    HourView()\\n                    HourView(hour: \\\"15:00\\\", icon: \\\"sun.dust.fill\\\", color: \\\"tuesday\\\")\\n                    HourView(hour: \\\"16:00\\\",icon: \\\"cloud.rain.fill\\\", color: \\\"thursday\\\")\\n                    HourView(hour: \\\"17:00\\\",icon: \\\"cloud.bolt.fill\\\", color: \\\"sunday\\\")\\n                    HourView(hour: \\\"18:00\\\",icon: \\\"snow\\\", color: \\\"mainCard\\\")\\n                }.frame(minWidth: 0, maxWidth: .infinity, minHeight: gr.size.height * 2 / 10)\\n                .padding(.horizontal)\\n                .background(Color.white)\\n                .cornerRadius(30)\\n                .padding()\\n\")), mdx(\"p\", null, \"As you can see, I\\u2019m reusing the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"HourView\"), \" to create the hourly forecast.\\nThe preview should show something like:\"), mdx(\"p\", null, mdx(\"img\", _extends({\n    parentName: \"p\"\n  }, {\n    \"src\": \"https://res.cloudinary.com/liquidcoder/image/upload/v1569523173/swiftui-weatherapp/nlevvaejvbdadwqpngyp.png\",\n    \"alt\": \"SwiftUI - CustomView\"\n  }))), mdx(\"p\", null, \"Your complete \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"DetailView.swift\"), \" file should look like this:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \"import SwiftUI\\n\\nstruct DetailView: View {\\n    \\n     @Binding var weather: Weather\\n    \\n    var body: some View {\\n        GeometryReader { gr in\\n            VStack(spacing: 20) {\\n                \\n                // Day text\\n                Text(self.weather.day).fontWeight(.bold)\\n                    .font(.system(size: 60))\\n                    .frame(height: gr.size.height * 1/10)\\n                    .minimumScaleFactor(0.5)\\n                    .foregroundColor(Color.white)\\n                \\n                // Weather image\\n                Image(systemName: self.weather.weatherIcon)\\n                    .resizable()\\n                    .foregroundColor(Color.white)\\n                    .frame(width: gr.size.height * 3 / 10, height: gr.size.height * 3 / 10)\\n                \\n                // Degrees texts\\n                VStack {\\n                    VStack(spacing: 20) {\\n                        Text(\\\"\\\\(self.weather.currentTemp)\\xB0\\\")\\n                            .font(.system(size: 50))\\n                            .foregroundColor(Color.white)\\n                            .fontWeight(.bold)\\n                            .frame(height: gr.size.height * 0.7/10)\\n                            .minimumScaleFactor(0.5)\\n                        \\n                        HStack(spacing: 40) {\\n                            Text(\\\"\\\\(self.weather.minTemp)\\xB0\\\")\\n                                .foregroundColor(Color(\\\"light-text\\\"))\\n                                .font(.title)\\n                                .minimumScaleFactor(0.5)\\n                            \\n                            Text(\\\"\\\\(self.weather.maxTemp)\\xB0\\\")\\n                                .foregroundColor(Color.white)\\n                                .font(.title)\\n                                .minimumScaleFactor(0.5)\\n                        }\\n                    }\\n                }\\n                \\n                // Hourly views\\n                HStack (spacing: 20){\\n                    HourView()\\n                    HourView(hour: \\\"15:00\\\", icon: \\\"sun.dust.fill\\\", color: \\\"tuesday\\\")\\n                    HourView(hour: \\\"16:00\\\",icon: \\\"cloud.rain.fill\\\", color: \\\"thursday\\\")\\n                    HourView(hour: \\\"17:00\\\",icon: \\\"cloud.bolt.fill\\\", color: \\\"sunday\\\")\\n                    HourView(hour: \\\"18:00\\\",icon: \\\"snow\\\", color: \\\"mainCard\\\")\\n                }.frame(minWidth: 0, maxWidth: .infinity, minHeight: gr.size.height * 2 / 10)\\n                .padding(.horizontal)\\n                .background(Color.white)\\n                .cornerRadius(30)\\n                .padding()\\n                    \\n            }.frame(minWidth: 0, maxWidth: .infinity, minHeight: gr.size.height, alignment: .bottom)\\n                    .background(Color(self.weather.color))\\n                    .clipShape(CustomShape(), style: FillStyle.init(eoFill: true, antialiased: true))\\n                \\n            \\n\\n        }\\n    }\\n}\\n\\nstruct CustomShape: Shape {\\n    func path(in rect: CGRect) -> Path {\\n        let cornerRadius:CGFloat = 40\\n        var path = Path()\\n        \\n        path.move(to:  CGPoint(x: 0, y: cornerRadius))\\n        path.addQuadCurve(to: CGPoint(x: cornerRadius, y: 0), control: CGPoint.zero)\\n        path.addLine(to: CGPoint(x: rect.width - cornerRadius, y: 0))\\n        path.addQuadCurve(to: CGPoint(x: rect.width, y: cornerRadius), control: CGPoint(x: rect.width , y: 0))\\n        path.addLine(to: CGPoint(x: rect.width, y: rect.height))\\n        path.addLine(to: CGPoint(x: 0, y: rect.height))\\n        path.closeSubpath()\\n\\n        return path\\n    }\\n}\\n\\nstruct HourView: View {\\n    var hour = \\\"14:00\\\"\\n    var icon = \\\"sun.max.fill\\\"\\n    var color = \\\"wednesday\\\"\\n    \\n    var body: some View {\\n        GeometryReader { gr in\\n            VStack{\\n                Text(self.hour).foregroundColor(Color(\\\"text\\\"))\\n                Image(systemName: self.icon)\\n                    .resizable()\\n                    .foregroundColor(Color(self.color))\\n                    .frame(width: gr.size.height * 1/3, height: gr.size.height * 1/3)\\n                \\n                Text(\\\"24\\xB0\\\")\\n                    .font(.system(size: 24))\\n                    .foregroundColor(Color(\\\"text\\\"))\\n                    .fontWeight(.semibold)\\n            }\\n        }.padding(.vertical, 30)\\n    }\\n}\\n\\n\\nstruct DetailView_Previews: PreviewProvider {\\n    static var previews: some View {\\n        DetailView(weather: .constant(Weather(id: 1, day: \\\"Monday\\\", weatherIcon:  \\\"sun.max\\\", currentTemp:  \\\"40\\\", minTemp: \\\"25\\\", maxTemp: \\\"69\\\", color: \\\"mainCard\\\")))\\n    }\\n}\\n\\n\")), mdx(\"p\", null, \"Let us now integrate our \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"DetailView\"), \" into the WeatherApp: \"), mdx(\"p\", null, \"We want to add a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"tapGesture\"), \" on each \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"SmallCardView\"), \" so that when we click on any of them, we show a correspondent details.\"), mdx(\"p\", null, \"Find the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"SmallCard\"), \" call and replace it with the following:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \"SmallCard(weather: weather).onTapGesture {\\n                            self.showDetails.toggle()\\n                            self.weather = weather\\n                        \\n                    }\\n\")), mdx(\"p\", null, \"and add this  on top of the file  below \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"weather\")), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \"@State private var showDetails = false\\n\")), mdx(\"p\", null, \"Everytime one of the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"SmallCard\"), \" is clicked, we want to toggle the  \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"showDetails\"), \"\\u2019 value.\"), mdx(\"p\", null, \"Add this below \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"showDetails\"), \": \"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \"    private var detailSize = CGSize(width: 0, height: UIScreen.main.bounds.height)\\n\\n\")), mdx(\"p\", null, \"And surround the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"MainCardView\"), \", the next 7 days \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Text\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ScrollView\"), \" with a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ScrollView(.vertical, sh owsIndicators: false)\\xA0\"), \" which makes it scrollable vertically.\"), mdx(\"p\", null, \"Now, put the newly added \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ScrollView\"), \" into a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ZStack\"), \" container and add the following code below the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ScrollView\"), \" inside the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ZStack\"), \" block:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \" DetailView(weather: $sampleData[selected])\\n                .offset( self.showDetails ? CGSize.zero : detailSize)\\n\")), mdx(\"p\", null, \"And this below \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"detailSize\"), \" property at the top:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \" @State private var sampleData = Weather.sampleData\\n\")), mdx(\"p\", null, \"As you may have noticed already, there is a gap at the bottom . To fix that add this modifier to the outer \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"VStack\"), \" container.\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \".edgesIgnoringSafeArea(.bottom)\\n\")), mdx(\"p\", null, \"To make the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"DetailView\"), \" animate beautifully surround these 2 calls inside \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"onTapGesture\"), \" with \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"withAnimation(.spring())\\xA0\"), \" like this:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \"  withAnimation(.spring()) {\\n                                        self.showDetails.toggle()\\n                                        self.weather = weather\\n                                    }\\n\")), mdx(\"h2\", null, \"Close Button\"), mdx(\"p\", null, \"In \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"DetailView.swift\"), \", surround the outer \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"VStack\"), \" with a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ZStack\"), \" and below the outer \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"VStack\"), \" add the following:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \"     // Close icon\\n                HStack {\\n                    Image(systemName: \\\"xmark\\\")\\n                        .resizable()\\n                        .foregroundColor(Color.red)\\n                        .frame(width: 20, height: 20)\\n                    \\n                }.padding(20\\n                ).background(Color.white)\\n                    .cornerRadius(100)\\n                    .offset(x: 0, y: -gr.size.height / 2)\\n                    .shadow(radius: 20)\\n                  \\n\")), mdx(\"p\", null, \"The important bit is  this   \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".offset(x: 0, y: -gr.size.height / 2)\"), \". When you add views in a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"VStack\"), \" container, they will be stacked on top of each other on the centre of the container (In this case, the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ZStack\"), \" container) . So, to position the at the top , with we move it up by 1/2 the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ZStack\"), \" height.\"), mdx(\"p\", null, \"Let\\u2019s now add \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"onTapGesture\"), \" to allow dismissal of the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"DetailView\"), \".\"), mdx(\"p\", null, \"First, add this \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"@Binding var showDetails: Bool\"), \"\\nto the top above body and  add this  \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"showDetails: .constant(false)\"), \" parameter in DetailView\\u2019s constructor inside \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"DetailView_Previews\"), \" \\u2018 block.\"), mdx(\"p\", null, \"Last,  add this modifier on Close icon \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"HStatck\"), \" after \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"shadow\"), \": \"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \" .onTapGesture {\\n                        withAnimation(.spring()) {\\n                            self.showDetails.toggle()\\n                        }\\n                }\\n\")), mdx(\"p\", null, \"You \\u2018ll get an error in \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"WeatherApp\"), \". Just add this parameter \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \",showDetails: self.$showDetails\"), \"  to the constructor.\"), mdx(\"p\", null, \"The last that you can do to make the transition look good is to show / hide a background blur based on the showDetails value. \"), mdx(\"p\", null, \"Add this below \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"detailSize\"), \" to the top:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \"  private var blurRadius = CGFloat(5.0)\\n\")), mdx(\"p\", null, \"Then, add this \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".blur(radius: showDetails ? blurRadius : 0)\"), \" to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"NavBarView\"), \", \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Picker\"), \" and the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ScrollView\"), \".\"), mdx(\"h2\", null, \"Drag dismissal\"), mdx(\"p\", null, \"Wouldn\\u2019t be nice if the user had a second and more intuitive option to dismiss  the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"DetailView\"), \" by dragging it down? Well, thanks to SwiftUI, implementing this behaviour is very simple. Let me show you how.\"), mdx(\"p\", null, \"First, add this line to the top of the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"WeatherApp\"), \" struct:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \"private var yOffset:CGFloat = 0.0\\n\")), mdx(\"p\", null, \"Then add this line in SmallCard tapGesture closure, below  self.weather = weather:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \"self.yOffset = 0.0\\n\")), mdx(\"p\", null, \"Last, replace the offset modifier on DetailView with the following:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \".offset(x: 0, y: showDetails ? yOffset : self.screenHeight)\\n.gesture(\\n        DragGesture().onChanged({ value in\\n            self.yOffset = value.translation.height\\n        }).onEnded({ value in\\n            withAnimation(.spring()) {\\n                if self.yOffset > self.screenHeight / 3 {\\n                    self.yOffset = self.screenHeight\\n                    self.showDetails = false\\n                } else {\\n                    self.yOffset = 0.0\\n                   \\n                }\\n            }\\n        })\\n    )\\n\\n\")), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Explanation\")), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"The yOffset value will be toggled between 0.0 and the full screen height.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"I then set the y offset of the DetailView to 0.0 if showDetails is true otherwise remove it by setting the y offset to full height screen\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"We set yOffset with the vertical translation because we only want to move the view vertically.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"We put the dismissal logic in a withAnimation block to animate the view smoothly.\")), mdx(\"p\", null, \"The rest of the code is ease to understand. Setting the yOffset to full screen height means the view will be dismissed whereas setting it to 0.0 makes it visible.\"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"Introduction Normally, I would start by telling you what swift UI is and blessed blah blah blah… However , I am not going to do that. If you are here, chances are you know already what Swift UI is.So, let’s just jump right into it, shall we? Requirements Xcode 12 or greater MacOS Catalina or greater…"}}}},"pageContext":{"slug":"weatherui-in-swiftui"}},"staticQueryHashes":["1047155515","1278771865","2517639938","2517639938","2537039069","3452127246","3566879862","3891201089","439617394","670651827"]}