Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I get a SwiftUI View to completely fill its superview?

Tags:

swift

swiftui

The following is supposed to create a Text whose bounds occupy the entire screen, but it seems to do nothing.

struct ContentView: View {
    var body: some View {
        Text("foo")
            .relativeSize(width: 1.0, height: 1.0)
            .background(Color.red)
    }
}

The following hack:

extension View {
    /// Causes the view to fill into its superview.
    public func _fill(alignment: Alignment = .center) -> some View {
        GeometryReader { geometry in
            return self.frame(
                width: geometry.size.width,
                height: geometry.size.height,
                alignment: alignment
            )
        }
    }
}

struct ContentView2: View {
    var body: some View {
        Text("foo")
            ._fill()
            .background(Color.red)
    }
}

seems to work however.

Is this a SwiftUI bug with relativeSize, or am I missing something?

like image 827
Vatsal Manot Avatar asked Dec 07 '22 11:12

Vatsal Manot


1 Answers

You need to watch WWDC 2019 Session 237: Building Custom Views with SwiftUI, because Dave Abrahams discusses this topic, and uses Text in his examples.

To restate briefly what Dave explains in detail:

  1. The parent (in this case, a root view created by the system and filling the screen) proposes a size to its child.
  2. The child chooses its own size, consuming as much or as little of the proposed size as it wants.
  3. The parent positions the child in the parent’s coordinate space based on various parameters including the size chosen by the child.

Thus you cannot force a small Text to fill the screen, because in step 2, the Text will refuse to consume more space than needed to fit its content.

Color.red is different: in step 2, it just returns the proposed size as its own size. We can call views like this “expandable”: they expand to fill whatever space they're offered.

ZStack is also different: in step 2, it asks its children for their sizes and picks its own size based on its children's sizes. We can call views like this “wrapping”: they wrap their children tightly.

So if you promote Color.red to be the “main” view returned by body, and put the Text in an overlay, your ContentView will behave like Color.red and be expandable:

struct ContentView: View {
    var body: some View {
        Color.red
            .overlay(Text("foo"))
    }
}

If you use a ZStack containing both Color.red and Text, the ZStack will wrap the Color.red, and thus take on its expandability:

struct ContentView: View {
    var body: some View {
        ZStack {
            Color.red
            Text("hello")
        }
    }
}
like image 75
rob mayoff Avatar answered Mar 28 '23 03:03

rob mayoff