Search code examples
swiftxcodeautolayoutconstraintsnslayoutconstraint

Added subview is positioned slightly off - how can I get it back into place?


UPDATE: Solved! While the contentMode for pianoNoteDisplayed and piano_background were indeed the same, apparently this wasn't true for the added subviews. I simply added the line subview.contentMode = superview.contentMode to the function vdmzz suggested, and now everything looks right on all 4 screen sizes.

There are two image views: one called "piano_background" holds a background image (a piano keyboard) and the other will be used to display highlighted notes. The second is constrained to the first:

constraints (the width constraint is probably unnecessary, because the leading and trailing constraints are already set, right?)

To display multiple highlighted keys, I am programmatically adding subviews to the piano_note view and activating the NSLayoutConstraints to get it into place (otherwise it shows up way out of position) like so:

pianoNoteDisplayed.image = nil

if !notesAlreadyAttempted.contains(currentUserAnswer) {

   let wrongNoteImageName = "large_\(currentUserAnswer)_wrong"
   let wrongNoteImage = UIImage(named: wrongNoteImageName)
   let wrongNoteImageView = UIImageView(image: wrongNoteImage!)

   wrongNoteImageView.translatesAutoresizingMaskIntoConstraints = false
   pianoNoteDisplayed.addSubview(wrongNoteImageView)

   NSLayoutConstraint.activate([
       wrongNoteImageView.widthAnchor.constraint(equalToConstant: pianoNoteDisplayed!.frame.width),
       wrongNoteImageView.heightAnchor.constraint(equalToConstant: pianoNoteDisplayed!.frame.height)
   ])

   }

   notesAlreadyAttempted.append(currentUserAnswer)
}

The issue is that the subview is displayed slightly off, and I can't seem to figure out why:

subview slightly off (as you can see, the highlight looks slightly compressed vertically.. the top lands correctly, but the bottom doesn't reach far enough by about 5px)

I have tried centering and constraining the subview in multiple ways, using suggestions from about 5 different answers on stack, and a few other articles I found. The images I am using (the piano background and the overlaying note highlight subview) are identical sizes. I have tried adding more or fewer constraints in the interface builder, and I have tried adding subviews to the original piano_background view instead of the second pianoNoteDisplayed view - same result. Using the pianoNoteDisplayed view itself to display the highlighted note works fine by the way:

image displaying correctly 1

image displaying correctly 2

And these are displayed using the usual .image method:

pianoNoteDisplayed.image = UIImage(named: "large_\(currentCorrectAnswer)_right")

Any suggestions for how to troubleshoot the issue further?


Solution

  • First of all, as far as I understood pianoNoteDisplayed doesn't need to be an UIImageView. Secondly, if you align piano_background and pianoNoteDisplayed by top, leading, trailing and bottom edges, one will be exactly on top of other. Or you could set them equal height, width and center positions. The problem with your current set of constraints is that piano_background's Y position is determined by Safe Area and therefore might defer from pianoNoteDisplayed's Y position.

    Try using this function:

    func addSameSize(subview: UIView, onTopOf superview: UIView) {
        superview.addSubview(subview)
    
        subview.translatesAutoresizingMaskIntoConstraints = false
    
        subview.centerXAnchor.constraint(equalTo: superview.centerXAnchor).isActive = true
        subview.centerYAnchor.constraint(equalTo: superview.centerYAnchor).isActive = true
        subview.widthAnchor.constraint(equalTo: superview.widthAnchor).isActive = true
        subview.heightAnchor.constraint(equalTo: superview.heightAnchor).isActive = true
    
        subview.contentMode = superview.contentMode
    }
    

    E.g.

    addSameSize(subview: wrongNoteImageView, onTopOf: pianoNoteDisplayed)
    

    It will add your image views exactly aligned on top of pianoNoteDisplayed view