-1

I am creating a horizontal stack view:

let hs = UIStackView(forAutoLayout: ());
hs.axis = .horizontal
hs.spacing = 0
hs.alignment = .top
hs.distribution = .fill
hs.layoutMargins = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
hs.isLayoutMarginsRelativeArrangement = true
hs.translatesAutoresizingMaskIntoConstraints = false

And then placing a couple children:

let label = UILabel(forAutoLayout: ());
hs.addArrangedSubview(label)
let image = UIImageView(frame: CGRect(x: 0, y: 0, width: 32, height: 32))
image.directionalLayoutMargins = NSDirectionalEdgeInsets(top: 64, leading: 0, bottom: 16, trailing: 16)
hs.addArrangedSubview(image)

The horizontal stack view layout looks right, however I added a layout margin to the image that seems to be ignored. Is there a way to set a margin around specific children in a horizontal stack view?

EDIT:

I get that I can throw the UIImageView inside another view and setup constraints. Or even easier just throw it in another stack view with EdgeInsets:

let imageStack = UIStackView(forAutoLayout: ());
imageStack.axis = .vertical
imageStack.alignment = .fill
imageStack.distribution = .fill
imageStack.directionalLayoutMargins = NSDirectionalEdgeInsets(top: 64, leading: 0, bottom: 16, trailing: 16)
imageStack.isLayoutMarginsRelativeArrangement = true
imageStack.translatesAutoresizingMaskIntoConstraints = false

imageStack.addArrangedSubview(image)

// add imageStack to horizontal stack view, instead of image
hs.addArrangedSubview(imageStack)

Adding to another view or playing with the image rect insets inside the UIImageView seems like overkill just to add margin or padding around a view. Coming from Android where I can just set padding and margin on any view including image views without wrapping in another element this just seems like a crazy amount of work for something so simple.

lostintranslation
  • 23,756
  • 50
  • 159
  • 262
  • Are you trying to "add top padding" to the image view? If so, that approach is incorrect. See: https://stackoverflow.com/questions/32551890/how-to-add-leading-padding-to-view-added-inside-an-uistackview – DonMag Aug 25 '20 at 12:11
  • So to add insets or margin to an imageview you have to put it in another view. Seems ridiculous, thanks iOS. – lostintranslation Aug 25 '20 at 13:15

1 Answers1

0

Not entirely sure what you're going for, but it sounds like you want:

  • horizontal stack view
  • label on the left
  • image view on the right
  • image view 32x32 its
  • image view top 64-pts from the label top

So, it would look like this:

enter image description here

The green outlines are showing the element frames (including the stack view).

If that's correct, you can do this by using .withAlignmentRectInsets() to modify the image at run-time.

Here's an example:

class StackTestViewController: UIViewController {
    
    let hs = UIStackView()
    let label = UILabel()
    let imageView = UIImageView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        hs.axis = .horizontal
        hs.spacing = 0
        hs.alignment = .top
        hs.distribution = .fill
        hs.isLayoutMarginsRelativeArrangement = true
        hs.translatesAutoresizingMaskIntoConstraints = false
        
        label.text = "Test Label"
        
        hs.addArrangedSubview(label)
        hs.addArrangedSubview(imageView)
        
        view.addSubview(hs)
        
        let g = view.safeAreaLayoutGuide
        
        NSLayoutConstraint.activate([
            
            hs.topAnchor.constraint(equalTo: g.topAnchor, constant: 80.0),
            hs.centerXAnchor.constraint(equalTo: g.centerXAnchor),
            
            imageView.widthAnchor.constraint(equalToConstant: 32.0),
            
            // we want the imageView to be 64-pts taller than its width
            imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor, constant: 64)
            
        ])
        
        if let img = UIImage(named: "swiftBlue64x64") {
            // use withAlignmentRectInsets to create a new image with 64-pts "empty space" on top
            imageView.image = img.withAlignmentRectInsets(UIEdgeInsets(top: -64, left: 0, bottom: 0, right: 0))
        }

        // so we can see the label frame
        label.backgroundColor = .yellow
        
    }
    
}

I used this 64x64 pixel image:

enter image description here


Edit

From Apple's docs:

Discussion

Use this property to specify the desired amount of space (measured in points) between the edges of this view and its subviews.

So, you can either apply that to a UIStackView as a whole or to a view that has subviews. Which should explain why it cannot be applied to a UIImageView (no subviews).

DonMag
  • 69,424
  • 5
  • 50
  • 86
  • Thank you. I updated the question, I suppose its really about a misunderstanding I think I had with directionalLayoutMargins. Seems like I should be able to just set margins on the ImageView. I get that I can wrap the UIIImageView in another view, or mess with the image insets inside the ImageView. But that seems like a workaround for the fact that one cannot just simply add margin around an ImageView. – lostintranslation Aug 25 '20 at 15:31