ImageView changes coordinates when I draw on that swift ios

Viewed 1194

I am following this tutorial, at the first when app launches everything is alright imagView is set where I set it. But the problem is when I start drawing on imageView the size of imageView resizes to ambiguous coordinates and it enlarges and occupy to much space result in ambiguous view, even I have set the auto layout. May be I am missing something, here is the code :

import UIKit
import CoreImage

class ViewController: UIViewController {

let imageNameIs = "BradPitImage"
var lastPoint = CGPoint.zero
var red: CGFloat = 0.0
var green: CGFloat = 0.0
var blue: CGFloat = 0.0
var brushWidth: CGFloat = 10.0
var opacity: CGFloat = 1.0
var swiped = false

@IBOutlet weak var tempImageView: UIImageView!
var tempImageViewtemp : UIImageView = UIImageView()

@IBOutlet weak var resetButton: UIButton!
@IBOutlet weak var saveButton: UIButton!

@IBAction func resetAct(_ sender: Any) {
    tempImageView.image = nil
    tempImageView.image = UIImage(named: imageNameIs)
}

override func viewDidLoad() {
    super.viewDidLoad()
    self.tempImageView.image = UIImage(named: imageNameIs)
}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    swiped = false
    if let touch = touches.first {
        lastPoint = touch.location(in: self.tempImageView)//view)
    }
}

func drawLineFrom(fromPoint: CGPoint, toPoint: CGPoint) {
    // 1
    UIGraphicsBeginImageContext(view.frame.size)//(tempImageView.frame.size) makes lines coluored of image
    let context = UIGraphicsGetCurrentContext()
    tempImageView.image?.draw(in: CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height))

    // 3
    context!.move(to: fromPoint)
    context!.addLine(to: toPoint)
    context!.setLineCap(.round)
    context!.setLineWidth(brushWidth)
    context!.setStrokeColor(red: red, green: green, blue: blue, alpha: 1)
    context!.setBlendMode(.normal)

    // 4
    context!.strokePath()
    //CGContextStrokePath(context!)

    // 5
    tempImageView.image = UIGraphicsGetImageFromCurrentImageContext()
    tempImageView.alpha = opacity
    UIGraphicsEndImageContext()
}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    swiped = true
    if let touch = touches.first {
        let currentPoint = touch.location(in: view)
        drawLineFrom(fromPoint: lastPoint, toPoint: currentPoint)
    // 7
    lastPoint = currentPoint
    }
}

 func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
    // 6
    swiped = true
    if let touch = touches.first as? UITouch {
        let currentPoint = touch.location(in: view)
        drawLineFrom(fromPoint: lastPoint, toPoint: currentPoint)
        // 7
        lastPoint = currentPoint
    }
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    if !swiped {
        //draw a single point
        drawLineFrom(fromPoint: lastPoint, toPoint: lastPoint)
    }
    UIGraphicsBeginImageContext(tempImageViewtemp.frame.size)
    tempImageViewtemp.image?.draw(in: CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height), blendMode: .normal, alpha: 1)
    tempImageView.image?.draw(in: CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height), blendMode: .normal, alpha: opacity)
    tempImageViewtemp.image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
  }
}

Here are images:

1) When Launches

enter image description here

2) When I start drawing

enter image description here

**Update According to DonMag's answer still ambiguous coordinates **

it work only for portrait orientation , the problem is when I do landscape mode and start drawing then image size reduces to top and there is increasing wide are empty at bottom, and then I do portrait then image became at the centre from top to bottom as more narrower I have added the screenshots new according to your code

1) from portrait to landscape at first time :

enter image description here

2) when I start drawing, it reduces the image and drawing becomes blur

enter image description here

Now when launches from landscape looks like this and drawing does does not reduces nor makes any blur:

enter image description here

But when turning to portrait image shrinks like this

enter image description here

2 Answers

Couple things...

  1. It looks like you have your image view set to Aspect Fit scale. That means your image is not the same size as the image view itself.

  2. You are mixing frame sizes of your image view and the root view.

Here is your code with a few modifications. Review the comments indicated by // ---. You should be able to replace your current code and run this as-is:

import UIKit
import CoreImage

extension UIView {
    // return content of view as a UIImage
    // can be used to get a copy of the rendered image view
    // e.g. the result of scaling setting

    func asImage() -> UIImage {
        let renderer = UIGraphicsImageRenderer(bounds: bounds)
        return renderer.image { rendererContext in
            layer.render(in: rendererContext.cgContext)
        }
    }

}

class ViewController: UIViewController {

    let imageNameIs = "BradPitImage"
    var lastPoint = CGPoint.zero
    var red: CGFloat = 0.0
    var green: CGFloat = 0.0
    var blue: CGFloat = 0.0
    var brushWidth: CGFloat = 10.0
    var opacity: CGFloat = 1.0
    var swiped = false

    @IBOutlet weak var tempImageView: UIImageView!
    var tempImageViewtemp : UIImageView = UIImageView()

    // ---
    // --- this will hold a copy of the resized image (for reset)
    var resizedImage: UIImage!

    @IBOutlet weak var resetButton: UIButton!
    @IBOutlet weak var saveButton: UIButton!

    @IBAction func resetAct(_ sender: Any) {
        // ---
        // --- reset using the resized image from viewDidAppear
        tempImageView.image = resizedImage
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        self.tempImageView.image = UIImage(named: imageNameIs)
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        // ---
        // --- create a resized copy of the original image
        // --- (handles aspect fit scale)
        resizedImage = tempImageView.asImage()
        tempImageView.image = resizedImage

    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        swiped = false
        if let touch = touches.first {
            // ---
            // --- get point ** in tempImageView **
            lastPoint = touch.location(in: self.tempImageView)  //view)
        }
    }

    func drawLineFrom(fromPoint: CGPoint, toPoint: CGPoint) {
        // 1

        // ---
        // --- get the bounds of tempImageView
        let rect = tempImageView.bounds

        // ---
        // --- use the bounds for the image context size
        UIGraphicsBeginImageContext(rect.size)

        let context = UIGraphicsGetCurrentContext()

        // ---
        // --- draw the image into the bounds rect
        tempImageView.image?.draw(in: rect)

        // 3
        context!.move(to: fromPoint)
        context!.addLine(to: toPoint)
        context!.setLineCap(.round)
        context!.setLineWidth(brushWidth)
        context!.setStrokeColor(red: red, green: green, blue: blue, alpha: 1)
        context!.setBlendMode(.normal)

        // 4
        context!.strokePath()
        //CGContextStrokePath(context!)

        // 5
        tempImageView.image = UIGraphicsGetImageFromCurrentImageContext()
        tempImageView.alpha = opacity
        UIGraphicsEndImageContext()
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        swiped = true
        if let touch = touches.first {

            // ---
            // --- get point ** in tempImageView **
            let currentPoint = touch.location(in: self.tempImageView)

            drawLineFrom(fromPoint: lastPoint, toPoint: currentPoint)

            // 7
            lastPoint = currentPoint
        }
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        if !swiped {
            //draw a single point
            drawLineFrom(fromPoint: lastPoint, toPoint: lastPoint)
        }

        // ---
        // don't know what you're doing with tempImageViewtemp,
        // but you probably want to change the sizing here to match elsewhere
        UIGraphicsBeginImageContext(tempImageViewtemp.frame.size)
        tempImageViewtemp.image?.draw(in: CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height), blendMode: .normal, alpha: 1)
        tempImageView.image?.draw(in: CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height), blendMode: .normal, alpha: opacity)
        tempImageViewtemp.image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
    }
}

Edit:

Here is a slightly different approach that may get you further on your way.

Instead of changing your image, change the size of the imageView to match the ratio of the image.

Storyboard layout:

enter image description here

The image view has constraints to remain centered in the white superview, and will be restricted to its width and height, maintaining proportion (aspect ratio).

The white view has constraints keeping it sized / positioned above the buttons.

Result - portrait orientation, with a circle drawn on left eye:

enter image description here

Result - device rotated to landscape orientation, with another circle drawn around the right eye:

enter image description here

You will also note in the code / comments that the "brushWidth" has to be adjusted based on the image height... using only 10-pts as the width will result in "thicker" lines when the image is shorter (landscape).

Here is the code:

//
//  DrawOnViewController.swift
//
//  Created by Don Mag on 11/7/18.
//

import UIKit

extension NSLayoutConstraint {
    /**
    Change multiplier constraint

    - parameter multiplier: CGFloat
    - returns: NSLayoutConstraint
    */
    func setMultiplier(multiplier:CGFloat) -> NSLayoutConstraint {

        NSLayoutConstraint.deactivate([self])

        let newConstraint = NSLayoutConstraint(
            item: firstItem as Any,
            attribute: firstAttribute,
            relatedBy: relation,
            toItem: secondItem,
            attribute: secondAttribute,
            multiplier: multiplier,
            constant: constant)

        newConstraint.priority = priority
        newConstraint.shouldBeArchived = self.shouldBeArchived
        newConstraint.identifier = self.identifier

        NSLayoutConstraint.activate([newConstraint])
        return newConstraint
    }
}

class DrawOnViewController: UIViewController {

    let imageNameIs = "BradPitImage"
    var lastPoint = CGPoint.zero
    var red: CGFloat = 0.0
    var green: CGFloat = 0.0
    var blue: CGFloat = 0.0
    var brushWidth: CGFloat = 10.0
    var opacity: CGFloat = 1.0
    var swiped = false

    @IBOutlet weak var tempImageView: UIImageView!
    var tempImageViewtemp : UIImageView = UIImageView()

    // ---
    // --- this will hold a reference to the image view's aspect ratio constraint
    @IBOutlet var imageViewAspectRatioConstraint: NSLayoutConstraint!

    // ---
    // --- this will be used to allow "brushWidth" to remain
    // --- constant when device is rotated
    var loadedImageHeight: CGFloat = 0.0

    // ---
    // --- this will be the "brushWidth" relative to
    // --- to the image's originial height
    var adjustedBrushWidth: CGFloat = 0.0

    @IBOutlet weak var resetButton: UIButton!
    @IBOutlet weak var saveButton: UIButton!

    @IBAction func resetAct(_ sender: Any) {
        if let img = UIImage(named: imageNameIs) {
            // ---
            // --- reset the imageView's image
            tempImageView.image = img
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        if let img = UIImage(named: imageNameIs) {

            let imgSize = img.size

            // ---
            // --- calculate the aspect ratio of the image
            let ratio = imgSize.width / imgSize.height

            // ---
            // --- change the image view's aspect ratio to match the image
            imageViewAspectRatioConstraint = imageViewAspectRatioConstraint.setMultiplier(multiplier: ratio)

            // ---
            // --- save original image height for brushWidth adjustment
            loadedImageHeight = imgSize.height

            // ---
            // --- set the imageView's image
            self.tempImageView.image = img

        }

    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        // ---
        // --- calculate brushWidth relative to image height scaled to image view height
        adjustedBrushWidth = brushWidth * loadedImageHeight / tempImageView.frame.height
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        swiped = false
        if let touch = touches.first {
            // ---
            // --- get point ** in tempImageView **
            lastPoint = touch.location(in: self.tempImageView)
        }
    }

    func drawLineFrom(fromPoint: CGPoint, toPoint: CGPoint) {
        // 1

        // ---
        // --- get the bounds of tempImageView
        let rect = tempImageView.bounds

        // ---
        // --- use the bounds for the image context size
        UIGraphicsBeginImageContext(rect.size)

        let context = UIGraphicsGetCurrentContext()

        // ---
        // --- draw the image into the bounds rect
        tempImageView.image?.draw(in: rect)

        // 3
        context!.move(to: fromPoint)
        context!.addLine(to: toPoint)
        context!.setLineCap(.round)

        // ---
        // --- set line width proportional to current imageView height
        // --- use adjustedBrushWidth set in viewDidAppear
//      context!.setLineWidth(brushWidth * rect.height / loadedImageHeight)
        context!.setLineWidth(adjustedBrushWidth * rect.height / loadedImageHeight)

        context!.setStrokeColor(red: red, green: green, blue: blue, alpha: 1)
        context!.setBlendMode(.normal)

        // 4
        context!.strokePath()
        //CGContextStrokePath(context!)

        // 5
        tempImageView.image = UIGraphicsGetImageFromCurrentImageContext()
        tempImageView.alpha = opacity
        UIGraphicsEndImageContext()
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        swiped = true
        if let touch = touches.first {

            // ---
            // --- get point ** in tempImageView **
            let currentPoint = touch.location(in: self.tempImageView)

            drawLineFrom(fromPoint: lastPoint, toPoint: currentPoint)

            // 7
            lastPoint = currentPoint
        }
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        if !swiped {
            //draw a single point
            drawLineFrom(fromPoint: lastPoint, toPoint: lastPoint)
        }

        // ---
        // don't know what you're doing with tempImageViewtemp,
        // but you probably want to change the sizing here to match elsewhere
        //      UIGraphicsBeginImageContext(tempImageViewtemp.frame.size)
        //      tempImageViewtemp.image?.draw(in: CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height), blendMode: .normal, alpha: 1)
        //      tempImageView.image?.draw(in: CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height), blendMode: .normal, alpha: opacity)
        //      tempImageViewtemp.image = UIGraphicsGetImageFromCurrentImageContext()
        //      UIGraphicsEndImageContext()
    }
}

And here is the storyboard with the required constraints:

<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14109" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="bvb-cu-Gcn">
    <device id="retina4_7" orientation="portrait">
        <adaptation id="fullscreen"/>
    </device>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
        <capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <scenes>
        <!--Draw On View Controller-->
        <scene sceneID="b4T-Zn-uEk">
            <objects>
                <viewController id="bvb-cu-Gcn" customClass="DrawOnViewController" customModule="SW4Temp" customModuleProvider="target" sceneMemberID="viewController">
                    <view key="view" contentMode="scaleToFill" id="fif-yb-Cf8">
                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <subviews>
                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="GU5-HP-5fd">
                                <rect key="frame" x="8" y="28" width="359" height="593"/>
                                <subviews>
                                    <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="1MQ-UT-8dQ">
                                        <rect key="frame" x="0.5" y="117.5" width="358.5" height="358.5"/>
                                        <color key="backgroundColor" red="1" green="0.1857388616" blue="0.57339501380000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                        <constraints>
                                            <constraint firstAttribute="width" secondItem="1MQ-UT-8dQ" secondAttribute="height" multiplier="1:1" id="xD6-8c-1AI"/>
                                        </constraints>
                                    </imageView>
                                </subviews>
                                <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                <constraints>
                                    <constraint firstItem="1MQ-UT-8dQ" firstAttribute="height" secondItem="GU5-HP-5fd" secondAttribute="height" priority="750" id="14p-DB-cE1"/>
                                    <constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="1MQ-UT-8dQ" secondAttribute="bottom" id="9A8-ey-EzV"/>
                                    <constraint firstItem="1MQ-UT-8dQ" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="GU5-HP-5fd" secondAttribute="leading" id="DbB-Mv-lEx"/>
                                    <constraint firstItem="1MQ-UT-8dQ" firstAttribute="top" relation="greaterThanOrEqual" secondItem="GU5-HP-5fd" secondAttribute="top" id="Eq0-pn-RKN"/>
                                    <constraint firstItem="1MQ-UT-8dQ" firstAttribute="centerY" secondItem="GU5-HP-5fd" secondAttribute="centerY" id="d9A-Un-06B"/>
                                    <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="1MQ-UT-8dQ" secondAttribute="trailing" id="dDp-HC-4pn"/>
                                    <constraint firstItem="1MQ-UT-8dQ" firstAttribute="centerX" secondItem="GU5-HP-5fd" secondAttribute="centerX" id="mqJ-4L-wQR"/>
                                </constraints>
                            </view>
                            <button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="1000" verticalHuggingPriority="1000" horizontalCompressionResistancePriority="1000" verticalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="QmC-N4-hsp">
                                <rect key="frame" x="20" y="629" width="39" height="30"/>
                                <color key="backgroundColor" red="0.99953407049999998" green="0.98835557699999999" blue="0.47265523669999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                <state key="normal" title="Reset"/>
                                <connections>
                                    <action selector="resetAct:" destination="bvb-cu-Gcn" eventType="touchUpInside" id="5ug-cY-v6A"/>
                                </connections>
                            </button>
                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="BWU-rw-ePF">
                                <rect key="frame" x="321" y="629" width="34" height="30"/>
                                <color key="backgroundColor" red="0.99953407049999998" green="0.98835557699999999" blue="0.47265523669999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                <state key="normal" title="Save"/>
                                <connections>
                                    <action selector="resetAct:" destination="bvb-cu-Gcn" eventType="touchUpInside" id="JkQ-M7-brY"/>
                                </connections>
                            </button>
                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="sia-an-mGC">
                                <rect key="frame" x="168.5" y="629" width="37" height="30"/>
                                <color key="backgroundColor" red="0.99953407049999998" green="0.98835557699999999" blue="0.47265523669999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                <state key="normal" title="Color"/>
                            </button>
                        </subviews>
                        <color key="backgroundColor" red="1" green="0.83234566450000003" blue="0.47320586440000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                        <constraints>
                            <constraint firstItem="mdN-5j-gOT" firstAttribute="bottom" secondItem="sia-an-mGC" secondAttribute="bottom" constant="8" id="33Q-9r-cik"/>
                            <constraint firstItem="mdN-5j-gOT" firstAttribute="bottom" secondItem="BWU-rw-ePF" secondAttribute="bottom" constant="8" id="9Xw-wi-cSH"/>
                            <constraint firstItem="mdN-5j-gOT" firstAttribute="trailing" secondItem="BWU-rw-ePF" secondAttribute="trailing" constant="20" id="Bwp-Uu-Qt7"/>
                            <constraint firstItem="GU5-HP-5fd" firstAttribute="top" secondItem="mdN-5j-gOT" secondAttribute="top" constant="8" id="E63-DW-lHC"/>
                            <constraint firstItem="mdN-5j-gOT" firstAttribute="trailing" secondItem="GU5-HP-5fd" secondAttribute="trailing" constant="8" id="GDF-yb-Zhr"/>
                            <constraint firstItem="GU5-HP-5fd" firstAttribute="leading" secondItem="mdN-5j-gOT" secondAttribute="leading" constant="8" id="Pw9-SO-lkd"/>
                            <constraint firstItem="mdN-5j-gOT" firstAttribute="bottom" secondItem="QmC-N4-hsp" secondAttribute="bottom" constant="8" id="TKU-Ht-CmQ"/>
                            <constraint firstItem="QmC-N4-hsp" firstAttribute="leading" secondItem="mdN-5j-gOT" secondAttribute="leading" constant="20" id="Uwc-k4-TP4"/>
                            <constraint firstItem="QmC-N4-hsp" firstAttribute="top" secondItem="GU5-HP-5fd" secondAttribute="bottom" constant="8" id="ute-8t-Q2P"/>
                            <constraint firstItem="sia-an-mGC" firstAttribute="centerX" secondItem="fif-yb-Cf8" secondAttribute="centerX" id="zXd-nm-Goa"/>
                        </constraints>
                        <viewLayoutGuide key="safeArea" id="mdN-5j-gOT"/>
                    </view>
                    <connections>
                        <outlet property="imageViewAspectRatioConstraint" destination="xD6-8c-1AI" id="KWT-aA-gtN"/>
                        <outlet property="tempImageView" destination="1MQ-UT-8dQ" id="brX-iu-UgX"/>
                    </connections>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="zoz-eV-W9h" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="48.799999999999997" y="624.73763118440786"/>
        </scene>
    </scenes>
</document>
tempImageView.image?.draw(in: CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height))

This line of code specifies to do the drawing with self.view's size. This will cause the image to get distorted as the aspect ratio of image will not be the same as that of self.view.

Replacing these above line with below line should fix the problem

 tempImageView.image?.draw(in: tempImageView.bounds)

Make similar change in where-ever you are using 'draw' method

Related