SwiftUI Weekly Observations #2

During development in SwiftUI, it is an often question to arise – how to size each element on the screen?

The Problem

There are basically two choices: 

  1. keep the dimensions of the element within itself
  2. set the dimensions during initialisation of the element

Here’s an example of how two approaches look like separately:

Hardcoded Size

struct CircleButton : View {

  var action : () -> Void

  var body : some View {
    // size is set according to the hardcoded values
    Button(action: self.action) {
      Circle()
        .background(Color.blue)
    
    }
    .frame(width: Constants.width, height: Constants.height)
  
  }

  enum Constants {
    static let width : CGFloat = 300
    static let height : CGFloat = 80
  
  }

}

GeometryReader-based Size

struct CircleButton : View {

  var action : () -> Void

  var body : some View {
    // size is according to the one specified by either .frame modifier of the CircleButton
    // instance, or by the View that contains the button
    GeometryReader { geometry in
      Button(action: self.action) {
        Circle()
          .background(Color.blue)
      
      }
      .frame(width: geometry.size.width, height: geometry.size.height)
 
    }
  
  }

}

But what if sometimes the button is needed according to its default size, and sometimes – according to the .frame modifier from “outside”?

I have discovered a fairly handy practice of keeping the balance between these two worlds.

In order to overcome this problem, we need a `struct` that would have a default value, and also would be available as an argument of the default initializer of the `CircleButton`:

struct CircleButton : View {

  var action : () -> Void
  @State var size : Size = .template
  
  var body : some View {
    // size is set according to the hardcoded values
    Button(action: self.action) {
      Circle()
        .background(Color.blue)
    
    }
    .frame(width: self.size.width, height: self.size.height)
  
  }

  struct Size {
    var width : CGFloat
    var height : CGFloat
    static var template : Self { Size(width: 300, height: 80) }
  
  }

}

Now we can use either default size of the button:

CircleButton(action: { print("Hello!" })

or size that we need it to occupy:

CircleButton(action: { print("Hello, resized button!", size: .init(width: 180, height: 40)) }

I hope this post helps you to solve your problem thus leading to speedup in your learning curve of the SwiftUI!

See you next week!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s