Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift - Keep UIImageView in the same position on rotate

Tags:

ios

swift

I have UIWebView and in its scrollView, I added a UIImageView like so:

self.webview.scrollView.addSubview(image)

my problem is when I rotate my device from portrait to landscape the UIImageView does not stay at the position I originally set it to on the scrollView, I understand the width and height of the screen change, I guess what I am trying to do it change the the position on my UIImageView so it appears it did not change.

I have this method in place:

 override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {

        var newScrollViewFrame = webview.scrollView.frame
        var newFrame = webview.frame

        newFrame.size.width = size.width
        newFrame.size.height = size.height

        newScrollViewFrame.size.width = size.width
        newScrollViewFrame.size.height = size.height

        webview.frame = newFrame
        webview.scrollView.frame = newScrollViewFrame

    }

The current code inside this method just resize the UIWebView and its scroll view, but not the UIImageViews in the scrollView

Any help would be appreciated.

Thanks,

I have tried the following:

for views in webview.scrollView.subviews
        {
            if(views.isKindOfClass(UIImageView))
            {
                views.transform = CGAffineTransformMakeRotation(CGFloat(M_PI)/2);

            }
        }

but this puts on UIImageView sideways on rotate

Here is how I am adding the webview to the view:

webview = UIWebView()

        webview.frame = self.view.bounds
        webview.scrollView.frame = webview.frame

        webview.userInteractionEnabled = true
        webview.scalesPageToFit = true
        webview.becomeFirstResponder()
        webview.delegate = self
        webview.scrollView.delegate = self
        self.view.addSubview(webview)

I have half solved my problem, but doing the following:

webview.translatesAutoresizingMaskIntoConstraints = false

let horizontalConstraint = NSLayoutConstraint(item: webview, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: view, attribute: NSLayoutAttribute.CenterX, multiplier: 1, constant: 0)
        view.addConstraint(horizontalConstraint)

        let verticalConstraint = NSLayoutConstraint(item: webview, attribute: NSLayoutAttribute.CenterY, relatedBy: NSLayoutRelation.Equal, toItem: view, attribute: NSLayoutAttribute.CenterY, multiplier: 1, constant: 0)
        view.addConstraint(verticalConstraint)

        let widthConstraint = NSLayoutConstraint(item: webview, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 0.1, constant: 500)
        view.addConstraint(widthConstraint)

        let heightConstraint = NSLayoutConstraint(item: webview, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 500)
        view.addConstraint(heightConstraint)

However now, the UIWebView is not full width or height :( Sooooo close.

I also tried this, but the UIImageView do not remain in the same spot.

let left = NSLayoutConstraint(item: self.webView, attribute: .Left, relatedBy: .Equal, toItem: self.view, attribute: .Left, multiplier: 1, constant: 0) 

let right = NSLayoutConstraint(item: self.webView, attribute: .Right, relatedBy: .Equal, toItem: self.view, attribute: .Right, multiplier: 1, constant: 0) 

let top = NSLayoutConstraint(item: self.webView, attribute: .Top, relatedBy: .Equal, toItem: self.view, attribute: .Top, multiplier: 1, constant: 0) 

let bottom = NSLayoutConstraint(item: self.webView, attribute: .Bottom, relatedBy: .Equal, toItem: self.view, attribute: .Bottom, multiplier: 1, constant: 0) 

NSLayoutConstraint.activateConstraints([top, bottom, left, right])

I have also tried adding constraints to the UIImageView

let stampView:StampAnnotation = StampAnnotation(imageIcon: UIImage(named: "approved.png"), location: CGPointMake(currentPoint.x, currentPoint.y))
            self.webview.scrollView.addSubview(stampView)

            let left = NSLayoutConstraint(item: stampView, attribute: .Left, relatedBy: .Equal, toItem: self.webview.scrollView, attribute: .Left, multiplier: 1, constant: 0)

            let right = NSLayoutConstraint(item: stampView, attribute: .Right, relatedBy: .Equal, toItem: self.webview.scrollView, attribute: .Right, multiplier: 1, constant: 0)

            let top = NSLayoutConstraint(item: stampView, attribute: .Top, relatedBy: .Equal, toItem: self.webview.scrollView, attribute: .Top, multiplier: 1, constant: 0)

            let bottom = NSLayoutConstraint(item: stampView, attribute: .Bottom, relatedBy: .Equal, toItem: self.webview.scrollView, attribute: .Bottom, multiplier: 1, constant: 0)

            NSLayoutConstraint.activateConstraints([top, bottom, left, right])

same result, UIImageView does not stay in the same spot.

UPDATE

My webview:

webview = UIWebView()
        webview.userInteractionEnabled = true
        webview.scalesPageToFit = true
        webview.becomeFirstResponder()
        webview.delegate = self
        webview.scrollView.delegate = self
        self.view.addSubview(webview)

        webview.loadRequest(NSURLRequest(URL:url))
        webview.gestureRecognizers = [pinchRecognizer, panRecognizer]
        webview.translatesAutoresizingMaskIntoConstraints = false

        let left = NSLayoutConstraint(item: webview, attribute: .Left, relatedBy: .Equal, toItem: self.view, attribute: .Left, multiplier: 1, constant: 0)

        let right = NSLayoutConstraint(item: webview, attribute: .Right, relatedBy: .Equal, toItem: self.view, attribute: .Right, multiplier: 1, constant: 0)

        let top = NSLayoutConstraint(item: webview, attribute: .Top, relatedBy: .Equal, toItem: self.view, attribute: .Top, multiplier: 1, constant: 0)

        let bottom = NSLayoutConstraint(item: webview, attribute: .Bottom, relatedBy: .Equal, toItem: self.view, attribute: .Bottom, multiplier: 1, constant: 0)

        NSLayoutConstraint.activateConstraints([top, bottom, left, right])

UIImageView

stampView.translatesAutoresizingMaskIntoConstraints = false

            let left = NSLayoutConstraint(item: stampView, attribute: .Left, relatedBy: .Equal, toItem: self.webview.scrollView, attribute: .Left, multiplier: 1, constant: 100)

            let right = NSLayoutConstraint(item: stampView, attribute: .Right, relatedBy: .Equal, toItem: self.webview.scrollView, attribute: .Right, multiplier: 1, constant: 0)

            let top = NSLayoutConstraint(item: stampView, attribute: .Top, relatedBy: .Equal, toItem: self.webview.scrollView, attribute: .Top, multiplier: 1, constant: 100)

            let bottom = NSLayoutConstraint(item: stampView, attribute: .Bottom, relatedBy: .Equal, toItem: self.webview.scrollView, attribute: .Bottom, multiplier: 1, constant: 0)

            let width = NSLayoutConstraint(item: stampView, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 0.1, constant: 150)

            let height = NSLayoutConstraint(item: stampView, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 73)

            NSLayoutConstraint.activateConstraints([left, right, top, bottom, width, height])

I just need to figure out the math to have this device be at the position of touch. (in percentages ?)

like image 748
user979331 Avatar asked Jul 21 '16 18:07

user979331


2 Answers

I think these things are always easier to do without using autolayout. To do this, I recomend using viewDidLayoutSubviews(). Here is my code:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    webView.frame = view.bounds

    let screen = UIScreen.mainScreen().fixedCoordinateSpace

    //These values will give a rect half the size of the screen and centered.
    let width = screen.bounds.width / 2
    let height = screen.bounds.height / 2
    let x = (screen.bounds.width - width) / 2
    let y = (screen.bounds.height - height) / 2
    let absoluteRect = CGRect(x: x, y: y, width: width, height: height)

    let stampRect = screen.convertRect(absoluteRect, toCoordinateSpace: webView)
    stampView.frame = stampRect

    //Change the orientation of the image
    switch UIDevice.currentDevice().orientation {
    case .LandscapeLeft:
        stampView.image = UIImage(CGImage:originalImage.CGImage!, scale:originalImage.scale, orientation: UIImageOrientation.Left)
        break
    case .LandscapeRight:
        stampView.image = UIImage(CGImage:originalImage.CGImage!, scale:originalImage.scale, orientation: UIImageOrientation.Right)
        break
    default:
        stampView.image = UIImage(CGImage:originalImage.CGImage!, scale:originalImage.scale, orientation: UIImageOrientation.Up)
        break
    }
}

I am doing several things here...First I set the webViewFrame. Next, it is helpful here to define an absolute coordinate system relative to your screen. When the phone orientation changes, the values of screen will not change (allowing you to keep your imageView in the same place.) Next I define the desired frame for the stampView, then convert the absolute frame into its equivalent inside your scrollView (it's superView) and assign it to the stampView. Finally, if you want the image in your stampView to always be oriented correctly, you need to change the image orientation. CGAffineTransformMakeRotation only works if your view is square, but we can make it more general by actually changing the imageOrientation within the stampView. This requires a property for the original image:

let originalImage = UIImage(named: "image_name")!

Finally, because viewWillLayoutSubViews() is not called for landscape to landscape transitions, do the following:

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
    view.setNeedsLayout()
}

This code doesn't make for the prettiest transitions, it should help with your layout problems.

like image 114
CarlNathan Avatar answered Nov 15 '22 21:11

CarlNathan


To make the UIImageView keep the same size and stay in the same position relative to the UIWebView, you can set your views to be a percentage of their respective superView. Here I've created some mock views that illustrate the basic idea:

import UIKit

class ViewController: UIViewController {

//we create some views for testing and create references to the size and points we need for positioning
let webView = UIWebView()
let image = UIImageView()
let image2 = UIImageView()
var imageViewSize = CGSize()
var imageViewCenter = CGPoint()

override func viewDidLoad() {
    super.viewDidLoad()

    //set webView frame
    webView.frame = self.view.bounds
    webView.backgroundColor = UIColor.clearColor()
    webView.userInteractionEnabled = true
    webView.scalesPageToFit = true
    webView.opaque = false
    self.view.addSubview(webView)

    //we will hold onto the original size of the UIImageView for later
    imageViewSize = CGSizeMake(webView.bounds.size.width * 0.2, webView.bounds.size.height * 0.1)

    //mock UIImageView
    image.backgroundColor = UIColor.orangeColor()
    image.frame.size = CGSizeMake(imageViewSize.width, imageViewSize.height)
    image.center = CGPointMake(webView.bounds.size.width / 2, webView.bounds.size.height / 2)
    webView.scrollView.addSubview(image)

    //I've created a subset image to illustrate the orientation
    image2.backgroundColor = UIColor.whiteColor()
    image2.frame = CGRectMake(10, 10, 10, 10)
    image.addSubview(image2)
}

And then change the frame when rotated (Notice that the position on the UIImageView and the width of the UIWebView are set relative to the height of their superViews and vice versa since the device is rotated):

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
    image.frame.size = CGSizeMake(imageViewSize.width, imageViewSize.height)
    image.center = CGPointMake(webView.bounds.size.height / 2, webView.bounds.size.width / 2)
    webView.frame = CGRectMake(0, 0, self.view.bounds.size.height, self.view.bounds.size.width)
    webView.scrollView.contentSize = webView.frame.size
}
}

Since you are creating the position and sizes of your views as percentages of their superViews, the code will play nicer with different device sizes (i.e. iPhone/iPad).

like image 41
jsondwyer Avatar answered Nov 15 '22 21:11

jsondwyer