{"id":58627,"date":"2023-09-28T20:18:59","date_gmt":"2023-09-28T14:48:59","guid":{"rendered":"https:\/\/www.tothenew.com\/blog\/?p=58627"},"modified":"2024-01-02T17:37:06","modified_gmt":"2024-01-02T12:07:06","slug":"advance-layout-techniques-in-swiftui","status":"publish","type":"post","link":"https:\/\/www.tothenew.com\/blog\/advance-layout-techniques-in-swiftui\/","title":{"rendered":"Advance Layout Techniques in\u00a0SwiftUI"},"content":{"rendered":"<p class=\"graf graf--p\">SwiftUI offers a range of tools and capabilities that can truly elevate our layouts. Throughout this article, We will explore advanced layout techniques, including <strong class=\"markup--strong markup--p-strong\">GeometryReader<\/strong>, <strong class=\"markup--strong markup--p-strong\">preference keys, and layout priorities. <\/strong>These techniques help us to create layouts that are not only responsive and dynamic but also visually stunning in our SwiftUI applications. So, let\u2019s dive right in and unlock the full potential of SwiftUI\u2019s advanced layout capabilities.<\/p>\n<h2 class=\"graf graf--h3\">Responsive Layouts with GeometryReader<\/h2>\n<p class=\"graf graf--p\"><strong class=\"markup--strong markup--p-strong\"><br \/>\n<\/strong>In SwiftUI, the GeometryReader view is like a container that gives us access to important information about the size and position of its parent view. This can be really helpful when we want to create layouts that work well on different devices and screen sizes.<\/p>\n<p class=\"graf graf--p\">With GeometryReader, we can find out things like how much space is available and where things are located. We can use <em class=\"markup--em markup--p-em\">frame<\/em>, <em class=\"markup--em markup--p-em\">safeAreaInsets,<\/em> and <em class=\"markup--em markup--p-em\">size<\/em> modifiers with geometry readers. This allows us to adjust the size and position of our child&#8217;s views to fit the available space perfectly. Using GeometryReader makes it easier to create layouts that automatically adapt to different devices and orientations.<\/p>\n<p class=\"graf graf--p\">This is similar to UIKit\u2019s UIScreen and UIView classes, which provide access to information about the screen size and available space. Let\u2019s understand GeometryReader with a simple example &#8211;<\/p>\n<figure class=\"graf graf--figure\"><img decoding=\"async\" class=\"graf-image\" src=\"\/blog\/wp-ttn-blog\/uploads\/2024\/01\/1QmUCOJ1s5Mox7ghwussCQg.png\" data-image-id=\"1*QmUCOJ1s5Mox7ghwussCQg.png\" data-width=\"2714\" data-height=\"1654\" \/><\/figure>\n<p class=\"graf graf--p\">In ContentView, we use GeometryReader to access the available space information. It helps arrange the content in a vertical stack using VStack. Inside the VStack, we have an HStack with three Rectangle views that represent columns. Each Rectangle is sized equally using geometry.size.width \/ 3.<\/p>\n<p class=\"graf graf--p\">To accommodate the safe area, we add a top padding of geometry.safeAreaInsets.top to the HStack. This ensures that the columns are positioned below any notches or status bars. We then add a Spacer to push the remaining content towards the bottom of the screen. Below the Spacer, we display the safe area insets using Text views styled appropriately. The Text views are placed in an HStack with a grey background and rounded corners.<br \/>\nBy leveraging GeometryReader and geometry.safeAreaInsets, our layout adjusts to the safe area, ensuring a consistent and user-friendly experience on different devices.<\/p>\n<h2 class=\"graf graf--h3\"><strong class=\"markup--strong markup--h3-strong\">Handling Data with PreferenceKeys<\/strong><\/h2>\n<p class=\"graf graf--p\">SwiftUI\u2019s PreferenceKeys are an excellent way for views to communicate with their parent or ancestor views without needing complicated callbacks or delegates. They streamline communication within our view hierarchy, making it easy to create interactive and dynamic user interfaces with minimal code.<\/p>\n<p class=\"graf graf--p\">In UIKit, achieving similar functionality as PreferenceKeys can be more complex, often involving delegates, notifications, or callbacks. While UIKit provides ways to pass data between views, they often require more boilerplate code and are less straightforward than SwiftUI\u2019s PreferenceKeys. The data collected using PreferenceKeys can be used for various purposes, such as adjusting layout, updating UI components, or sharing information across different parts of the app.<\/p>\n<p class=\"graf graf--p\">Let\u2019s explore how PreferenceKeys work with a step-by-step code example:<\/p>\n<p class=\"graf graf--p\">Let\u2019s suppose we have a list, and when we tap on an item in the list, it will navigate to the detail view. The detail view displays the selected item\u2019s name and an option that allows us to select or deselect it. When we select or deselect an item in the detail view, the background color of the selected item in the list should be updated accordingly.<\/p>\n<p class=\"graf graf--p\"><strong class=\"markup--strong markup--p-strong\">Create a Custom PreferenceKey<\/strong>: We start by defining a custom PreferenceKey, which acts like a data collector. In our example, it collects background color preferences.<\/p>\n<figure class=\"graf graf--figure\"><img decoding=\"async\" class=\"graf-image\" src=\"\/blog\/wp-ttn-blog\/uploads\/2024\/01\/1t4nrt4WytzdvfSVUR36nGQ.png\" data-image-id=\"1*t4nrt4WytzdvfSVUR36nGQ.png\" data-width=\"2254\" data-height=\"392\" \/><\/figure>\n<p class=\"graf graf--p\">Now, let\u2019s understand how <strong class=\"markup--strong markup--p-strong\">PreferenceKey<\/strong> operates:<\/p>\n<ul class=\"postList\">\n<li class=\"graf graf--li\"><strong class=\"markup--strong markup--li-strong\">defaultValue<\/strong>: This sets the initial value for the preference data. It\u2019s like the starting point for accumulating values as we move through the view hierarchy. In our case, we set it to nil as the default background color preference.<\/li>\n<li class=\"graf graf--li\"><strong class=\"markup--strong markup--li-strong\">reduce<\/strong> <strong>Method<\/strong>: The reduce method tells SwiftUI how to merge preference values when they\u2019re encountered at different levels of the view hierarchy. It has two essential parts:<\/li>\n<li class=\"graf graf--li\"><strong class=\"markup--strong markup--li-strong\">val<\/strong><strong>ue (inout)<\/strong>: This is like the current collection of data.<\/li>\n<li class=\"graf graf--li\"><strong class=\"markup--strong markup--li-strong\">nextValue<\/strong><strong> (closure)<\/strong>: It provides the next piece of data to add to the collection.<\/li>\n<\/ul>\n<p class=\"graf graf--p\">The reduce method gets called for each child view using\u00a0<code class=\"markup--code markup--p-code\">.preference<\/code>. It allows us to gather values from multiple child views and merge them into a single preference value that&#8217;s accessible to the parent view. This is where we specify how to update or replace the previous collection of data (value) with the new data (nextValue).<\/p>\n<p class=\"graf graf--p\">PreferenceKeys plays a crucial role in data flow within SwiftUI. <code class=\"markup--code markup--p-code\">defaultValue<\/code> sets the initial state and <code class=\"markup--code markup--p-code\">reduce<\/code>determines how values are combined as they travel up the view hierarchy. This mechanism efficiently manages data communication between views.<\/p>\n<p class=\"graf graf--p\">In our example code, we have three essential structs:<\/p>\n<p class=\"graf graf--p\">1. ListItemView:<\/p>\n<figure class=\"graf graf--figure\"><img decoding=\"async\" class=\"graf-image\" src=\"\/blog\/wp-ttn-blog\/uploads\/2024\/01\/1PnEYf6V5_vtNAXc_5ohkQw.png\" data-image-id=\"1*PnEYf6V5_vtNAXc_5ohkQw.png\" data-width=\"2250\" data-height=\"970\" \/><\/figure>\n<ul class=\"postList\">\n<li class=\"graf graf--li\"><code class=\"markup--code markup--li-code\">ListItemView<\/code> represents an item in a list.<\/li>\n<li class=\"graf graf--li\">It displays the item\u2019s name and updates its background color based on selection.<\/li>\n<li class=\"graf graf--li\">Tapping on an item toggles its selection state.<\/li>\n<li class=\"graf graf--li\">The background color is determined by whether the item is in the set of selected items (<code class=\"markup--code markup--li-code\">selectedItems<\/code>).<\/li>\n<li class=\"graf graf--li\">The <code class=\"markup--code markup--li-code\">onTapGesture<\/code> handler toggles the selection and updates the <code class=\"markup--code markup--li-code\">selectedItems<\/code> set.<\/li>\n<\/ul>\n<p class=\"graf graf--p\">2. ContentView:<\/p>\n<figure class=\"graf graf--figure\"><img decoding=\"async\" class=\"graf-image\" src=\"\/blog\/wp-ttn-blog\/uploads\/2024\/01\/1xmnrVDTSMnAyUe0GPMWfCA.png\" data-image-id=\"1*xmnrVDTSMnAyUe0GPMWfCA.png\" data-width=\"2272\" data-height=\"1232\" \/><\/figure>\n<ul class=\"postList\">\n<li class=\"graf graf--li\"><code class=\"markup--code markup--li-code\">ContentView<\/code> manages the main view of the app.<\/li>\n<li class=\"graf graf--li\">It displays a list of items that we can tap to view details.<\/li>\n<li class=\"graf graf--li\">The <code class=\"markup--code markup--li-code\">selectedItems<\/code> state keeps track of which items are selected.<\/li>\n<li class=\"graf graf--li\">It uses a <code class=\"markup--code markup--li-code\">NavigationView<\/code> to enable navigation between views.<\/li>\n<li class=\"graf graf--li\">The <code class=\"markup--code markup--li-code\">List<\/code> displays each item as a <code class=\"markup--code markup--li-code\">ListItemView<\/code>.<\/li>\n<li class=\"graf graf--li\">The\u00a0<code class=\"markup--code markup--li-code\">.onPreferenceChange<\/code> modifier listens for changes in the background color preference.<\/li>\n<\/ul>\n<p class=\"graf graf--p\">3. DetailView:<\/p>\n<figure class=\"graf graf--figure\"><img decoding=\"async\" class=\"graf-image\" src=\"\/blog\/wp-ttn-blog\/uploads\/2024\/01\/1eJtVftDjMM4UI1uaJINXnQ.png\" data-image-id=\"1*eJtVftDjMM4UI1uaJINXnQ.png\" data-width=\"2254\" data-height=\"1040\" \/><\/figure>\n<ul class=\"postList\">\n<li class=\"graf graf--li\"><code class=\"markup--code markup--li-code\">DetailView<\/code> displays the details of a selected item.<\/li>\n<li class=\"graf graf--li\">It shows the item\u2019s name and a button to select or deselect it.<\/li>\n<li class=\"graf graf--li\">The button title (\u201cSelect\u201d or \u201cDeselect\u201d) changes based on whether the item is selected.<\/li>\n<li class=\"graf graf--li\">Tapping the button updates the <code class=\"markup--code markup--li-code\">selectedItems<\/code> set.<\/li>\n<\/ul>\n<p class=\"graf graf--p\">In this code, we can select items in the list, and the detail view will show the selected item. The preference key is used to communicate the background color of the selected item from the detail view to the content view. This allows us to update the background color of the selected item in the list without having to pass the color explicitly.<\/p>\n<figure class=\"graf graf--figure\"><img decoding=\"async\" class=\"graf-image\" src=\"\/blog\/wp-ttn-blog\/uploads\/2024\/01\/1PCN3wl8t_LBpi0LREacsPA.png\" data-image-id=\"1*PCN3wl8t_LBpi0LREacsPA.png\" data-width=\"3338\" data-height=\"1134\" \/><\/figure>\n<p class=\"graf graf--p\">As shown in the image above, initially, we selected \u201cItem 2\u201d and then clicked on \u201cItem 3\u201d to navigate to its detail page. There, we selected \u201cItem 3\u201d and then returned. In the last part of the image, we can observe that both the previously selected item and the item selected on the detail page (i.e., \u201cItem 3\u201d) are displayed as selected in the Parent View.<\/p>\n<p class=\"graf graf--p\">See, how the preference key works here:<\/p>\n<ol class=\"postList\">\n<li class=\"graf graf--li\">The <code class=\"markup--code markup--li-code\">DetailView<\/code> sets the <code class=\"markup--code markup--li-code\">BackgroundColorPreferenceKey<\/code> preference to the background color of the selected item.<\/li>\n<li class=\"graf graf--li\">The <code class=\"markup--code markup--li-code\">ContentView<\/code> listens for changes to the <code class=\"markup--code markup--li-code\">BackgroundColorPreferenceKey<\/code> preference.<\/li>\n<li class=\"graf graf--li\">When the <code class=\"markup--code markup--li-code\">BackgroundColorPreferenceKey<\/code> preference changes, the <code class=\"markup--code markup--li-code\">ContentView<\/code> updates the background color of the selected item in the list.<\/li>\n<\/ol>\n<p class=\"graf graf--p\">We can also use the <code class=\"markup--code markup--p-code\">@published<\/code> protocol to communicate the background color of the selected item from the <code class=\"markup--code markup--p-code\">DetailView<\/code>to the <code class=\"markup--code markup--p-code\">ContentView<\/code> But Preference keys are a better choice for communicating data between views in most cases because they offer a number of advantages over the <code class=\"markup--code markup--p-code\">@published<\/code> protocol. First, preference keys are more performant, especially for large and complex views. Second, preference keys are declarative, which means that you don&#8217;t need to write any additional code to handle the data flow, and preference keys are reusable.<\/p>\n<p class=\"graf graf--p\">In summary, PreferenceKeys provides a way for child views to communicate with their parent views in SwiftUI. This is especially useful when we need to send data from a child view to its parent without passing the data explicitly through the view hierarchy. PreferenceKeys are a powerful tool for enhancing data flow between views, making our code more reusable and maintainable.<\/p>\n<h2 class=\"graf graf--h3\">Layout Priorities<\/h2>\n<p class=\"graf graf--p\">Layout priorities help us to control how views are sized and positioned in parent containers. They become vital when space is limited, deciding which views get more or less space. let\u2019s consider this example &#8211;<\/p>\n<figure class=\"graf graf--figure\"><img decoding=\"async\" class=\"graf-image\" src=\"\/blog\/wp-ttn-blog\/uploads\/2024\/01\/1M2iu1dN9u-R5kygDT4gBw.png\" data-image-id=\"1*M2iu1dN9u--R5kygDT4gBw.png\" data-width=\"2746\" data-height=\"1736\" \/><\/figure>\n<p class=\"graf graf--p\">In this example, there are four Text views with different lengths and priorities. Views get priorities using\u00a0.layoutPriority(_:) with higher values for higher priority. Default is 0, but we can modify it.<\/p>\n<p class=\"graf graf--p\"><strong class=\"markup--strong markup--p-strong\">Our priorities:<\/strong><\/p>\n<p class=\"graf graf--p\">&#8211; \u201cHigh Priority View\u201d: 1 (highest)<\/p>\n<p class=\"graf graf--p\">&#8211; \u201cMedium Priority View\u201d: 0.5 (medium)<\/p>\n<p class=\"graf graf--p\">&#8211; \u201cLow Priority View\u201d: 0.1 (lowest)<\/p>\n<p class=\"graf graf--p\">&#8211; \u201cAdditional View\u201d: 0 (lowest)<\/p>\n<p class=\"graf graf--p\"><strong class=\"markup--strong markup--p-strong\">Effects of Layout Priorities<\/strong>:<\/p>\n<p class=\"graf graf--p\">In spacious layouts, views show full content, and priorities have minimal impact. Limited space demonstrates their importance.<\/p>\n<p class=\"graf graf--p\">&#8211; \u201cLow Priority View\u201d shows full content first.<\/p>\n<ul class=\"postList\">\n<li class=\"graf graf--li graf--startsWithDoubleQuote\">\u201cAdditional View\u201d is also visible without competition.<\/li>\n<li class=\"graf graf--li graf--startsWithDoubleQuote\">\u201cMedium Priority View\u201d truncates before \u201cHigh Priority View\u201d due to higher priority (0.5 vs. 1).<\/li>\n<li class=\"graf graf--li graf--startsWithDoubleQuote\">\u201cHigh Priority View\u201d truncates only with extremely limited space.<\/li>\n<\/ul>\n<p class=\"graf graf--p\">Layout priorities are crucial in SwiftUI\u2019s advanced layouts. Essential content remains visible, while non-essential may get truncated or compressed. Understanding priorities enables responsive layouts for better user experiences.<\/p>\n<p class=\"graf graf--p\">In conclusion, SwiftUI\u2019s advanced layout features are the key to creating responsive and visually stunning apps. Keep coding and unlocking the full potential of SwiftUI!<\/p>\n<div class=\"ap-custom-wrapper\"><\/div><!--ap-custom-wrapper-->","protected":false},"excerpt":{"rendered":"<p>SwiftUI offers a range of tools and capabilities that can truly elevate our layouts. Throughout this article, We will explore advanced layout techniques, including GeometryReader, preference keys, and layout priorities. These techniques help us to create layouts that are not only responsive and dynamic but also visually stunning in our SwiftUI applications. So, let\u2019s dive [&hellip;]<\/p>\n","protected":false},"author":1648,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"iawp_total_views":73},"categories":[1400],"tags":[5460],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/58627"}],"collection":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/users\/1648"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/comments?post=58627"}],"version-history":[{"count":3,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/58627\/revisions"}],"predecessor-version":[{"id":59666,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/58627\/revisions\/59666"}],"wp:attachment":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/media?parent=58627"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/categories?post=58627"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/tags?post=58627"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}