Close Menu
  • Home
  • AI
  • Big Data
  • Cloud Computing
  • iOS Development
  • IoT
  • IT/ Cybersecurity
  • Tech
    • Nanotechnology
    • Green Technology
    • Apple
    • Software Development
    • Software Engineering

Subscribe to Updates

Get the latest technology news from Bigteetechhub about IT, Cybersecurity and Big Data.

    What's Hot

    Working with @Generable and @Guide in Foundation Models

    July 18, 2025

    Navigating the labyrinth of forks

    July 18, 2025

    OpenAI unveils ‘ChatGPT agent’ that gives ChatGPT its own computer to autonomously use your email and web apps, download and create files for you

    July 18, 2025
    Facebook X (Twitter) Instagram
    Facebook X (Twitter) Instagram
    Big Tee Tech Hub
    • Home
    • AI
    • Big Data
    • Cloud Computing
    • iOS Development
    • IoT
    • IT/ Cybersecurity
    • Tech
      • Nanotechnology
      • Green Technology
      • Apple
      • Software Development
      • Software Engineering
    Big Tee Tech Hub
    Home»iOS Development»Working with percentages in SwiftUI layout – Ole Begemann
    iOS Development

    Working with percentages in SwiftUI layout – Ole Begemann

    big tee tech hubBy big tee tech hubMarch 24, 2025017 Mins Read
    Share Facebook Twitter Pinterest Copy Link LinkedIn Tumblr Email Telegram WhatsApp
    Follow Us
    Google News Flipboard
    Working with percentages in SwiftUI layout – Ole Begemann
    Share
    Facebook Twitter LinkedIn Pinterest Email Copy Link


    SwiftUI’s layout primitives generally don’t provide relative sizing options, e.g. “make this view 50 % of the width of its container”. Let’s build our own!

    Use case: chat bubbles

    Consider this chat conversation view as an example of what I want to build. The chat bubbles always remain 80 % as wide as their container as the view is resized:

    The chat bubbles should become 80 % as wide as their container. Download video

    Building a proportional sizing modifier

    1. The Layout

    We can build our own relative sizing modifier on top of the Layout protocol. The layout multiplies its own proposed size (which it receives from its parent view) with the given factors for width and height. It then proposes this modified size to its only subview. Here’s the implementation (the full code, including the demo app, is on GitHub):

    /// A custom layout that proposes a percentage of its
    /// received proposed size to its subview.
    ///
    /// - Precondition: must contain exactly one subview.
    fileprivate struct RelativeSizeLayout: Layout {
        var relativeWidth: Double
        var relativeHeight: Double
    
        func sizeThatFits(
            proposal: ProposedViewSize, 
            subviews: Subviews, 
            cache: inout ()
        ) -> CGSize {
            assert(subviews.count == 1, "expects a single subview")
            let resizedProposal = ProposedViewSize(
                width: proposal.width.map { $0 * relativeWidth },
                height: proposal.height.map { $0 * relativeHeight }
            )
            return subviews[0].sizeThatFits(resizedProposal)
        }
    
        func placeSubviews(
            in bounds: CGRect, 
            proposal: ProposedViewSize, 
            subviews: Subviews, 
            cache: inout ()
        ) {
            assert(subviews.count == 1, "expects a single subview")
            let resizedProposal = ProposedViewSize(
                width: proposal.width.map { $0 * relativeWidth },
                height: proposal.height.map { $0 * relativeHeight }
            )
            subviews[0].place(
                at: CGPoint(x: bounds.midX, y: bounds.midY), 
                anchor: .center, 
                proposal: resizedProposal
            )
        }
    }
    

    Notes:

    • I made the type private because I want to control how it can be used. This is important for maintaining the assumption that the layout only ever has a single subview (which makes the math much simpler).

    • Proposed sizes in SwiftUI can be nil or infinity in either dimension. Our layout passes these special values through unchanged (infinity times a percentage is still infinity). I’ll discuss below what implications this has for users of the layout.

    2. The View extension

    Next, we’ll add an extension on View that uses the layout we just wrote. This becomes our public API:

    extension View {
        /// Proposes a percentage of its received proposed size to `self`.
        public func relativeProposed(width: Double = 1, height: Double = 1) -> some View {
            RelativeSizeLayout(relativeWidth: width, relativeHeight: height) {
                // Wrap content view in a container to make sure the layout only
                // receives a single subview. Because views are lists!
                VStack { // alternatively: `_UnaryViewAdaptor(self)`
                    self
                }
            }
        }
    }
    

    Notes:

    • I decided to go with a verbose name, relativeProposed(width:height:), to make the semantics clear: we’re changing the proposed size for the subview, which won’t always result in a different actual size. More on this below.

    • We’re wrapping the subview (self in the code above) in a VStack. This might seem redundant, but it’s necessary to make sure the layout only receives a single element in its subviews collection. See Chris Eidhof’s SwiftUI Views are Lists for an explanation.

    Usage

    The layout code for a single chat bubble in the demo video above looks like this:

    let alignment: Alignment = message.sender == .me ? .trailing : .leading
    chatBubble
        .relativeProposed(width: 0.8)
        .frame(maxWidth: .infinity, alignment: alignment)
    

    The outermost flexible frame with maxWidth: .infinity is responsible for positioning the chat bubble with leading or trailing alignment, depending on who’s speaking.

    You can even add another frame that limits the width to a maximum, say 400 points:

    let alignment: Alignment = message.sender == .me ? .trailing : .leading
    chatBubble
        .frame(maxWidth: 400)
        .relativeProposed(width: 0.8)
        .frame(maxWidth: .infinity, alignment: alignment)
    

    Here, our relative sizing modifier only has an effect as the bubbles become narrower than 400 points. In a wider window the width-limiting frame takes precedence. I like how composable this is!

    80 % won’t always result in 80 %

    If you watch the debugging guides I’m drawing in the video above, you’ll notice that the relative sizing modifier never reports a width greater than 400, even if the window is wide enough:

    Working with percentages in SwiftUI layout – Ole Begemann

    The relative sizing modifier accepts the actual size of its subview as its own size.

    This is because our layout only adjusts the proposed size for its subview but then accepts the subview’s actual size as its own. Since SwiftUI views always choose their own size (which the parent can’t override), the subview is free to ignore our proposal. In this example, the layout’s subview is the frame(maxWidth: 400) view, which sets its own width to the proposed width or 400, whichever is smaller.

    Understanding the modifier’s behavior

    Proposed size ≠ actual size

    It’s important to internalize that the modifier works on the basis of proposed sizes. This means it depends on the cooperation of its subview to achieve its goal: views that ignore their proposed size will be unaffected by our modifier. I don’t find this particularly problematic because SwiftUI’s entire layout system works like this. Ultimately, SwiftUI views always determine their own size, so you can’t write a modifier that “does the right thing” (whatever that is) for an arbitrary subview hierarchy.

    nil and infinity

    I already mentioned another thing to be aware of: if the parent of the relative sizing modifier proposes nil or .infinity, the modifier will pass the proposal through unchanged. Again, I don’t think this is particularly bad, but it’s something to be aware of.

    Proposing nil is SwiftUI’s way of telling a view to become its ideal size (fixedSize does this). Would you ever want to tell a view to become, say, 50 % of its ideal width? I’m not sure. Maybe it’d make sense for resizable images and similar views.

    By the way, you could modify the layout to do something like this:

    1. If the proposal is nil or infinity, forward it to the subview unchanged.
    2. Take the reported size of the subview as the new basis and apply the scaling factors to that size (this still breaks down if the child returns infinity).
    3. Now propose the scaled size to the subview. The subview might respond with a different actual size.
    4. Return this latest reported size as your own size.

    This process of sending multiple proposals to child views is called probing. Lots of built-in containers views do this too, e.g. VStack and HStack.

    Nesting in other container views

    The relative sizing modifier interacts in an interesting way with stack views and other containers that distribute the available space among their children. I thought this was such an interesting topic that I wrote a separate article about it: How the relative size modifier interacts with stack views.

    A blue, a green, and a yellow rectangle in a horizontal line. The blue rectangle is 100 units wide, the other two 250 units each.

    The code

    The complete code is available in a Gist on GitHub.

    Digression: Proportional sizing in early SwiftUI betas

    The very first SwiftUI betas in 2019 did include proportional sizing modifiers, but they were taken out before the final release. Chris Eidhof preserved a copy of SwiftUI’s “header file” from that time that shows their API, including quite lengthy documentation.

    I don’t know why these modifiers didn’t survive the beta phase. The release notes from 2019 don’t give a reason:

    The relativeWidth(_:), relativeHeight(_:), and relativeSize(width:height:) modifiers are deprecated. Use other modifiers like frame(minWidth:idealWidth:maxWidth:minHeight:idealHeight:maxHeight:alignment:) instead. (51494692)

    I also don’t remember how these modifiers worked. They probably had somewhat similar semantics to my solution, but I can’t be sure. The doc comments linked above sound straightforward (“Sets the width of this view to the specified proportion of its parent’s width.”), but they don’t mention the intricacies of the layout algorithm (proposals and responses) at all.

    containerRelativeFrame

    Update May 1, 2024: Apple introduced the containerRelativeFrame modifier for its 2023 OSes (iOS 17/macOS 14). If your deployment target permits it, this can be a good, built-in alternative.

    Note that containerRelativeFrame behaves differently than my relativeProposed modifier as it computes the size relative to the nearest container view, whereas my modifier uses its proposed size as the reference. The SwiftUI documentation somewhat vaguely lists the views that count as a container for containerRelativeFrame. Notably, stack views don’t count!

    Check out Jordan Morgan’s article Modifier Monday: .containerRelativeFrame(_ axes:) (2022-06-26) to learn more about containerRelativeFrame.



    Source link

    Begemann layout Ole percentages SwiftUI working
    Follow on Google News Follow on Flipboard
    Share. Facebook Twitter Pinterest LinkedIn Tumblr Email Copy Link
    tonirufai
    big tee tech hub
    • Website

    Related Posts

    Working with @Generable and @Guide in Foundation Models

    July 18, 2025

    Deep dive into Swift frameworks

    July 17, 2025

    How to parse JSON in Swift using Codable protocol?

    July 16, 2025
    Add A Comment
    Leave A Reply Cancel Reply

    Editors Picks

    Working with @Generable and @Guide in Foundation Models

    July 18, 2025

    Navigating the labyrinth of forks

    July 18, 2025

    OpenAI unveils ‘ChatGPT agent’ that gives ChatGPT its own computer to autonomously use your email and web apps, download and create files for you

    July 18, 2025

    Big milestone for the future of quantum computing.

    July 18, 2025
    Advertisement
    About Us
    About Us

    Welcome To big tee tech hub. Big tee tech hub is a Professional seo tools Platform. Here we will provide you only interesting content, which you will like very much. We’re dedicated to providing you the best of seo tools, with a focus on dependability and tools. We’re working to turn our passion for seo tools into a booming online website. We hope you enjoy our seo tools as much as we enjoy offering them to you.

    Don't Miss!

    Working with @Generable and @Guide in Foundation Models

    July 18, 2025

    Navigating the labyrinth of forks

    July 18, 2025

    Subscribe to Updates

    Get the latest technology news from Bigteetechhub about IT, Cybersecurity and Big Data.

      • About Us
      • Contact Us
      • Disclaimer
      • Privacy Policy
      • Terms and Conditions
      © 2025 bigteetechhub.All Right Reserved

      Type above and press Enter to search. Press Esc to cancel.