Search code examples
iosswiftframe

Set UIView frame equal to the frame of another view from a different superview


I'm creating UIViews that are in different superviews, but I want to set the frames to be equal to each-other.

enter image description here

This is what I've tried so far

let globalPoint = self.view.convert(subViewOfOuterView.frame.origin, to: nil)
let frame = CGRect(x: globalPoint.x, y: globalPoint.y, width: self.subViewOfOuterView.frame.width, height: self.subViewOfOuterView.frame.height)
subViewOfInnerUIView.frame = frame

But my view ends up looking like this

enter image description here


Solution

  • Using the object names you provided, here is an example that creates a 2nd view, also a UIButton (subViewOfInnerUIView), that will have the same frame as the 1st button (drawButton). Both buttons are subviews of different superviews within the main view. I used a UIButton so I could label the view, but any other subclass of UIView whose frame can be set will also work.

    Note that since they have the same frames, aside from having the same size, they also have the same position relative to their superviews.

    This should also work even if the objects in question reside in subviews that are several layers deep, or shallow. It should not matter.

    The example can be recreated in a single view playground in the latest XCode. Hope this helps!

    //: A UIKit based Playground for presenting user interface
    
    import UIKit
    import PlaygroundSupport
    
    class MyViewController : UIViewController {
        override func loadView() {
            let view = UIView()
            view.backgroundColor = .white
            self.view = view
    
            let subView1 = UIView(frame: CGRect(x: 40, y: 250, width: 300, height: 300))
            subView1.backgroundColor = .red
            view.addSubview(subView1)
    
            let subView2 = UIView(frame: CGRect(x: 20, y: 50, width: 340, height: 100))
            subView2.backgroundColor = .green
            view.addSubview(subView2)
    
            let drawButton = UIButton(frame: CGRect(x: subView1.frame.width / 2 - 50, y: 25, width: 150, height: 50))
            drawButton.backgroundColor = .blue
            drawButton.setTitle("DRAW BTN", for: .normal)
            subView1.addSubview(drawButton)
    
            let subViewOfInnerUIView = UIButton()
            subViewOfInnerUIView.setTitle("DRAW BTN2", for: .normal)
            subViewOfInnerUIView.backgroundColor = .brown
            subView2.addSubview(subViewOfInnerUIView)
    
            let frame = view.convert(drawButton.frame, to: nil)
            subViewOfInnerUIView.frame = frame
        }
    }
    // Present the view controller in the Live View window
    PlaygroundPage.current.liveView = MyViewController()
    

    Here's the updated code for making the two views overlap. I animated the change so it's clear, and also commented specific frames to try and explain how it was done:

    //: A UIKit based Playground for presenting user interface
    
    import UIKit
    import PlaygroundSupport
    
    class MyViewController : UIViewController {
        override func loadView() {
            let view = UIView()
            view.backgroundColor = .white
            self.view = view
    
            let subView1 = UIView(frame: CGRect(x: 40, y: 250, width: 300, height: 300))
            subView1.backgroundColor = .red
            view.addSubview(subView1)
    
            let subView2 = UIView(frame: CGRect(x: 20, y: 50, width: 340, height: 100))
            subView2.backgroundColor = .green
            view.addSubview(subView2)
    
            let drawButton = UIButton(frame: CGRect(x: subView1.frame.width / 2 - 50, y: 25, width: 150, height: 50))
            drawButton.backgroundColor = .blue
            drawButton.setTitle("DRAW BTN", for: .normal)
            subView1.addSubview(drawButton)
    
            let subViewOfInnerUIView = UIButton()
            subViewOfInnerUIView.setTitle("DRAW BTN2", for: .normal)
            subViewOfInnerUIView.backgroundColor = .brown
            subView2.addSubview(subViewOfInnerUIView)
    
            let frame1 = drawButton.frame
            let frame2 = subView1.convert(drawButton.frame, to: view)
            let frame3 = view.convert(frame2, to: subView2)
    
            print(frame1) // original drawButton frame in its superview
            print(frame2) // drawButton frame relative to the main view (self.view)
            print(frame3) // drawButton frame relative to subView2 (this is the frame you want)
    
            subViewOfInnerUIView.frame = view.convert(drawButton.frame, to: nil)
    
            UIView.animate(withDuration: 5.0, delay: 0.0, options: [.autoreverse, .repeat], animations: {
                subViewOfInnerUIView.frame = frame3
            }, completion: nil)
    
            subViewOfInnerUIView.frame = frame3
    
    
        }
    }
    // Present the view controller in the Live View window
    PlaygroundPage.current.liveView = MyViewController()
    

    The last line which sets subViewOfInnerUIView.frame to frame3 is basically what you want. You can probably create a function that does the work of these 3 lines to make it easier (taking the 2 subviews as arguments), although admittedly it may not be so simple if the view hierarchy is deep:

        let frame2 = subView1.convert(drawButton.frame, to: view)
        let frame3 = view.convert(frame2, to: subView2)
        subViewOfInnerUIView.frame = view.convert(drawButton.frame, to: nil)
    

    Hope this helps!