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
2) When I start drawing
**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 :
2) when I start drawing, it reduces the image and drawing becomes blur
Now when launches from landscape looks like this and drawing does does not reduces nor makes any blur:
But when turning to portrait image shrinks like this
Couple things...
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.
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:
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:
Result - device rotated to landscape orientation, with another circle drawn around the right eye:
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>