Need a Glowing Animation around a button - ios

In my application i have a big button. I want it to have a glowing effect around it.
Can it be done using animations?
I have tried using the image but it does not looks clean and appealing.

I had been working in your question and this is my results,I define a custom subclass of UIButton,adding a CABasicAnimation animating shadowRadius property, setting autoreverses to true and repeatCount to infinity
Code
//
// GlowingButton.swift
// NavigationButtonRotateQuestion
//
// Created by Reinier Melian on 01/07/2017.
// Copyright © 2017 Pruebas. All rights reserved.
//
import UIKit
#IBDesignable
class GlowingButton: UIButton {
#IBInspectable var animDuration : CGFloat = 3
#IBInspectable var cornerRadius : CGFloat = 5
#IBInspectable var maxGlowSize : CGFloat = 10
#IBInspectable var minGlowSize : CGFloat = 0
#IBInspectable var glowColor : UIColor = nil ?? UIColor.red
#IBInspectable var animateAlways : Bool = false
fileprivate var animating : Bool = false
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func awakeFromNib() {
super.awakeFromNib()
self.contentScaleFactor = UIScreen.main.scale
self.layer.masksToBounds = false
if(self.animateAlways){
self.setupButtonForContinueAnimation()
self.startAnimation()
}else{
self.setupButton()
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if(!self.animateAlways){
let layerAnimation = CABasicAnimation(keyPath: "shadowRadius")
layerAnimation.fromValue = minGlowSize
layerAnimation.toValue = maxGlowSize
layerAnimation.isAdditive = false
layerAnimation.duration = CFTimeInterval(animDuration/2)
layerAnimation.fillMode = CAMediaTimingFillMode.forwards
layerAnimation.isRemovedOnCompletion = false
self.layer.add(layerAnimation, forKey: "addGlowing")
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if(!self.animateAlways){
let layerAnimation = CABasicAnimation(keyPath: "shadowRadius")
layerAnimation.fromValue = maxGlowSize
layerAnimation.toValue = minGlowSize
layerAnimation.isAdditive = false
layerAnimation.duration = CFTimeInterval(animDuration/2)
layerAnimation.fillMode = CAMediaTimingFillMode.forwards
layerAnimation.isRemovedOnCompletion = false
self.layer.add(layerAnimation, forKey: "removeGlowing")
}
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
if(!self.animateAlways){
let layerAnimation = CABasicAnimation(keyPath: "shadowRadius")
layerAnimation.fromValue = maxGlowSize
layerAnimation.toValue = minGlowSize
layerAnimation.isAdditive = false
layerAnimation.duration = CFTimeInterval(animDuration/2)
layerAnimation.fillMode = CAMediaTimingFillMode.forwards
layerAnimation.isRemovedOnCompletion = false
self.layer.add(layerAnimation, forKey: "removeGlowing")
}
}
func setupButton()
{
self.layer.cornerRadius = cornerRadius
self.layer.shadowPath = CGPath(roundedRect: self.bounds, cornerWidth: cornerRadius, cornerHeight: cornerRadius, transform: nil)
self.layer.shadowRadius = 0
self.layer.shadowColor = self.glowColor.cgColor
self.layer.shadowOffset = CGSize.zero
self.layer.shadowOpacity = 1
}
func setupButtonForContinueAnimation()
{
self.setupButton()
self.layer.shadowRadius = maxGlowSize
}
func startAnimation()
{
let layerAnimation = CABasicAnimation(keyPath: "shadowRadius")
layerAnimation.fromValue = maxGlowSize
layerAnimation.toValue = minGlowSize
layerAnimation.autoreverses = true
layerAnimation.isAdditive = false
layerAnimation.duration = CFTimeInterval(animDuration/2)
layerAnimation.fillMode = CAMediaTimingFillMode.forwards
layerAnimation.isRemovedOnCompletion = false
layerAnimation.repeatCount = .infinity
self.layer.add(layerAnimation, forKey: "glowingAnimation")
}
func stopAnimations() {
self.layer.removeAllAnimations()
}
}
Edited: Added Inspectable attributes for better customization
Edited: Adapted to swift4 and added func to stop animation

Related

CALayer drawing outside of view

I have this behaviour with this code:
import UIKit
class Page: UIView {
var bezierMemory = [BezierRecord]()
var currentBezier: UIBezierPath = UIBezierPath()
var firstPoint: CGPoint = CGPoint()
var previousPoint: CGPoint = CGPoint()
var morePreviousPoint: CGPoint = CGPoint()
var previousCALayer: CALayer = CALayer()
var pointCounter = 0
var selectedPen: Pen = Pen(width: 3.0, strokeOpacity: 1, strokeColor: .red, fillColor: .init(gray: 0, alpha: 0.5), isPencil: true, connectsToStart: true, fillPencil: true)
enum StandardPageSizes {
case A4, LEGAL, LETTER
}
var firstCALayer = true
var pointsTotal = 0
override init(frame: CGRect) {
super.init(frame: .zero)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
guard let touch = touches.first else { return }
let point = touch.location(in: self)
firstPoint = point
pointCounter = 1
currentBezier = UIBezierPath()
currentBezier.lineWidth = selectedPen.width
selectedPen.getStroke().setStroke()
currentBezier.move(to: point)
previousPoint = point
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesMoved(touches, with: event)
guard let touch = touches.first else { return }
let point = touch.location(in: self)
pointCounter += 1
if (pointCounter == 3) {
let midpoint = CGPoint(x: (morePreviousPoint.x + point.x)/2.0, y: (morePreviousPoint.y + point.y)/2.0)
currentBezier.addQuadCurve(to: midpoint, controlPoint: morePreviousPoint)
let updatedCALayer = CAShapeLayer()
updatedCALayer.path = currentBezier.cgPath
updatedCALayer.lineWidth = selectedPen.width
updatedCALayer.opacity = selectedPen.strokeOpacity
updatedCALayer.strokeColor = selectedPen.getStroke().cgColor
updatedCALayer.fillColor = selectedPen.getFill()
if (firstCALayer) {
layer.addSublayer(updatedCALayer)
firstCALayer = false
} else {
layer.replaceSublayer(previousCALayer, with: updatedCALayer)
}
previousCALayer = updatedCALayer
pointCounter = 1
}
morePreviousPoint = previousPoint
previousPoint = point
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
guard let touch = touches.first else { return }
let point = touch.location(in: self)
if (pointCounter != 3) {
if (selectedPen.connectsToStart) {
currentBezier.addQuadCurve(to: firstPoint, controlPoint: previousPoint)
} else {
currentBezier.addQuadCurve(to: point, controlPoint: previousPoint)
}
let updatedCALayer = CAShapeLayer()
updatedCALayer.path = currentBezier.cgPath
updatedCALayer.lineWidth = selectedPen.width
updatedCALayer.opacity = selectedPen.strokeOpacity
updatedCALayer.strokeColor = selectedPen.getStroke().cgColor
updatedCALayer.fillColor = selectedPen.getFill()
if (firstCALayer) {
layer.addSublayer(updatedCALayer)
firstCALayer = false
} else {
// layer.setNeedsDisplay()
layer.replaceSublayer(previousCALayer, with: updatedCALayer)
}
}
firstCALayer = true
let bezierRecord = BezierRecord(bezier: currentBezier, strokeColor: selectedPen.getStroke(), fillColor: selectedPen.getFill(), strokeWidth: selectedPen.width)
bezierMemory.append(bezierRecord)
}
private func normPoint(point: CGPoint) -> CGPoint {
return CGPoint(x: point.x/frame.width, y: point.y/frame.height)
}
public class BezierRecord {
var bezier: UIBezierPath
var strokeColor: UIColor
var strokeWidth: CGFloat
var fillColor: CGColor
init(bezier: UIBezierPath, strokeColor: UIColor, fillColor: CGColor, strokeWidth: CGFloat) {
self.bezier = bezier
self.strokeColor = strokeColor
self.strokeWidth = strokeWidth
self.fillColor = fillColor
}
}
}
Really the only relevant parts are touchesMoved and touchesEnded, where CALayer's are dealt with. As you can see from the gif, I can draw outside the bounds of the Page (UIView) as long as I start drawing inside the bounds. I do not want this - what I would like is something where you can maintain a stroke outside of the bounds of the Page (as long as you start it on the Page), but the stroke wont appear outside of the Page. Any ideas?
EDIT: I should add that for these UIBezierCurves, (0, 0) is considered to be the top left of the Page (white), and not the entire view. Thus, for example, beziers that start on the page and continue on, are negative.
All you should need to do is set .clipsToBounds = true on the view.
One approach:
override init(frame: CGRect) {
super.init(frame: .zero)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
self.clipsToBounds = true
}
You could put self.clipsToBounds = true in both of the init funcs, but it is common practice (no pun intended) to add a "common init" func like this (it can be named whatever... this is how I do it). Frequently we have other "initial setup" code that we want called, and this avoids duplicating the code.

Delay when adding lines to a UIBezierPath in CAShapeLayer

I am trying to implement functionality to allow a user to draw in a UIImageView. My current code is as follows:
class DrawImageView: UIImageView {
// MARK: - Properties
private var lastPoint = CGPoint.zero
private var lineColor = UIColor.black
private var lineWidth: CGFloat = 10.0
private var opacity: CGFloat = 1.0
private var path: UIBezierPath?
private var swiped = false
private var tempShapeLayer: CAShapeLayer?
// MARK: - Initializers
override init(image: UIImage?) {
super.init(image: image)
isUserInteractionEnabled = true
clipsToBounds = true
}
#available(*, unavailable)
private init() {
fatalError("init() has not been implemented")
}
#available(*, unavailable)
private override init(frame: CGRect) {
fatalError("init(frame:) has not been implemented")
}
#available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// MARK: - Draw Methods
extension DrawImageView {
private func drawLine(from fromPoint: CGPoint, to toPoint: CGPoint) {
guard let tempShapeLayer = tempShapeLayer,
let cgPath = tempShapeLayer.path
else { return }
let path = UIBezierPath(cgPath: cgPath)
path.move(to: fromPoint)
path.addLine(to: toPoint)
tempShapeLayer.path = path.cgPath
setNeedsDisplay()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
swiped = false
lastPoint = touch.location(in: self)
let shapeLayer = CAShapeLayer()
shapeLayer.lineCap = .round
shapeLayer.path = path?.cgPath
shapeLayer.strokeColor = lineColor.cgColor
shapeLayer.lineWidth = lineWidth
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.frame = bounds
let path = UIBezierPath()
shapeLayer.path = path.cgPath
layer.addSublayer(shapeLayer)
shapeLayer.fillColor = UIColor.red.cgColor
tempShapeLayer = shapeLayer
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
swiped = true
let currentPoint = touch.location(in: self)
drawLine(from: lastPoint, to: currentPoint)
lastPoint = currentPoint
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if !swiped {
// Draw a single point
drawLine(from: lastPoint, to: lastPoint)
}
}
}
This image view is embedded inside a scroll view:
class ZoomImageVC: UIViewController {
// MARK: - Properties
private let bag = DisposeBag()
// MARK: - Views
private let scrollView = UIScrollView()
// Important this is init with image immediately
private let backgroundImageView = DrawImageView(image: UIImage(named: "test.jpg"))
// MARK: - View Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
// This will update on rotate too
updateMinZoomScaleForSize(view.bounds.size)
}
// MARK: - Setup
private func setupViews() {
view.backgroundColor = .white
scrollView.delegate = self
view.addSubview(scrollView)
scrollView.snp.makeConstraints {
$0.edges.equalToSuperview()
}
scrollView.rx.didZoom
.subscribe(with: self, onNext: { `self`, _ in
self.updateConstraintsForSize(self.view.bounds.size)
})
.disposed(by: bag)
scrollView.addSubview(backgroundImageView)
// This is DrawingImageView
backgroundImageView.snp.makeConstraints {
$0.edges.equalToSuperview()
}
}
private func updateMinZoomScaleForSize(_ size: CGSize) {
let widthScale = size.width / backgroundImageView.bounds.width
let heightScale = size.height / backgroundImageView.bounds.height
let minScale = min(widthScale, heightScale)
scrollView.minimumZoomScale = minScale
scrollView.zoomScale = minScale
}
private func updateConstraintsForSize(_ size: CGSize) {
let yOffset = max(0, (size.height - backgroundImageView.frame.height) / 2.0)
let xOffset = max(0, (size.width - backgroundImageView.frame.width) / 2.0)
backgroundImageView.snp.remakeConstraints {
$0.leading.trailing.equalToSuperview().offset(xOffset)
$0.top.bottom.equalToSuperview().offset(yOffset)
}
view.layoutIfNeeded()
}
}
// MARK: - UIScrollViewDelegate
extension ZoomImageVC: UIScrollViewDelegate {
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
backgroundImageView
}
}
The code works fine however there seems to be a small delay. For example if I start stroking a path there will be a slight delay until the path appears, continuing to draw the path seems to be smooth. I can scribble the path very fast and it keeps up. It's just the start of the drawing that lags.
If I just paint a single dot to the image view without dragging there also seems to be a small delay from me tapping to it appearing on the screen.
If I remove DrawImageView from the scroll view there seems to be no delay. Why would there be a delay when this is in a scroll view?
Ran your code and not sure what "delay" you're seeing.
However, you're doing a few things that you don't need to do...
You can use a single path and add lines as the touch moves... no need to moveTo / addLineTo every time.
Also, if you want the initial "dot" to show up as soon as the touch begins, you can moveTo / addLineTo the touch point on touchesBegan.
Here's your class, edited to (hopefully) get rid of the delay you're experiencing:
class DrawImageView: UIImageView {
// MARK: - Properties
private var lineColor = UIColor.black
private var lineWidth: CGFloat = 10.0
private var opacity: CGFloat = 1.0
// let's make these non-optional
private var drawingPath: UIBezierPath!
private var drawingShapeLayer: CAShapeLayer!
// MARK: - Initializers
override init(image: UIImage?) {
super.init(image: image)
isUserInteractionEnabled = true
clipsToBounds = true
}
#available(*, unavailable)
private init() {
fatalError("init() has not been implemented")
}
#available(*, unavailable)
private override init(frame: CGRect) {
fatalError("init(frame:) has not been implemented")
}
#available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
/// MARK: - Draw Methods
extension DrawImageView {
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let thisPoint = touch.location(in: self)
// create new shape layer
drawingShapeLayer = CAShapeLayer()
drawingShapeLayer.lineCap = .round
drawingShapeLayer.strokeColor = lineColor.cgColor
drawingShapeLayer.lineWidth = lineWidth
drawingShapeLayer.fillColor = UIColor.clear.cgColor
drawingShapeLayer.frame = bounds
// create new path
drawingPath = UIBezierPath()
// move to touch point
drawingPath.move(to: thisPoint)
// if we want the line (an initial "dot")
// to show up immediately
// add line to same touch point
drawingPath.addLine(to: thisPoint)
// assign the shape layer path
drawingShapeLayer.path = drawingPath.cgPath
// add the layer
layer.addSublayer(drawingShapeLayer)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let thisPoint = touch.location(in: self)
// add line to existing path
drawingPath.addLine(to: thisPoint)
// update path of shape layer
drawingShapeLayer.path = drawingPath.cgPath
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
// don't really need to do anything here
// unless you're taking some other action when
// touch ends
}
}

Touch delegates not called on view when z-index is set to non zero value

I am implementing the chat bubble feature. I want the chat bubble to be on top of all the view controller and shouldn't be affected by screen transition. So I decided to add it as subview to UIWindow so far it looks like
This works great, but if I present view controller instead of push then chat head goes behind the presented view controller. To avoid it I set ZIndex property of chat head layer
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let window = UIApplication.shared.keyWindow {
window.addSubview(chatHead)
chatHead.layer.zPosition = 1
}
}
But now though chat head appears on top of presented view controller, its touch delegates like touchesBegan, touchesMoved, touchesEnded nothing gets called when chat head is above presented ViewController
Adding code of chat head (lot of code might be unnecessary to this question, but just posting it so if someone would like to try it)
class ChatHeadView: UIView {
let headerWidth: CGFloat = 80.0
let headerHeight: CGFloat = 80.0
let sideInset: CGFloat = 10.0
let bottomInset: CGFloat = 10.0
var cancellationWindowShown = false
lazy var cancellationContainerRect: CGRect = {
var rect = self.cancelChatView.convert(self.cancelChatView.frame, to: nil)
rect.origin.x = rect.origin.x + 30
rect.size.height = 100
return rect
}()
lazy var cancellationThreshold: CGFloat = {
return self.keywindow.frame.maxY - (self.keywindow.frame.maxY / 3)
}()
lazy var keywindow: UIWindow = {
let window = UIApplication.shared.keyWindow ?? UIWindow()
return window
}()
lazy var cancelChatView: CancelChatView = {
if let cancelView = Bundle.main.loadNibNamed("CancelChatView", owner: nil, options: [:])?.first as? CancelChatView {
return cancelView
}
fatalError("Couldnt load")
}()
init() {
super.init(frame: CGRect(x: 0, y: 0, width: 80, height: 80))
self.layer.masksToBounds = true
self.layer.cornerRadius = 40
self.layer.backgroundColor = UIColor.red.cgColor
self.cancelChatView.frame = CGRect(x: 0, y: self.keywindow.frame.maxY - 140, width: self.keywindow.frame.width, height: 140)
self.cancelChatView.layer.opacity = 0.0
self.keywindow.addSubview(self.cancelChatView)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
debugPrint("Touch started")
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let location = touch.location(in: self.keywindow)
if location.y >= cancellationThreshold {
self.showCancellationWindow()
}
else {
removeCancellationView()
debugPrint("Removing Cancellation window")
}
self.center = location
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let location = touch.location(in: self.keywindow)
self.center = location
self.snap(to: location)
self.removeCancellationView()
}
}
private func snap(to point: CGPoint) {
var finalPoint:CGPoint = self.frame.origin
if self.cancellationContainerRect.intersects(self.convert(self.frame, to: nil)) {
self.removeFromSuperview()
}
else {
if finalPoint.x <= self.keywindow.center.x {
finalPoint.x = self.keywindow.frame.minX + sideInset
}
else if finalPoint.x > self.keywindow.center.x {
finalPoint.x = self.keywindow.frame.maxX - (sideInset + self.headerWidth)
}
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.6, options: .curveEaseInOut, animations: {
self.frame.origin = finalPoint
}, completion: nil)
}
}
private func showCancellationWindow() {
if cancellationWindowShown == false {
let animation = CABasicAnimation(keyPath: #keyPath(CALayer.opacity))
animation.fromValue = 0.0
animation.toValue = 0.6
animation.duration = 0.1
animation.fillMode = .forwards
animation.isRemovedOnCompletion = false
animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
self.cancelChatView.layer.add(animation, forKey: "reveal")
self.cancellationWindowShown = true
}
}
private func removeCancellationView() {
if cancellationWindowShown == true {
let animation = CABasicAnimation(keyPath: #keyPath(CALayer.opacity))
animation.toValue = 0.0
animation.duration = 0.1
animation.fillMode = .forwards
animation.isRemovedOnCompletion = false
animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
self.cancelChatView.layer.add(animation, forKey: "hide")
cancellationWindowShown = false
}
}
}
Issue Gif (touch not working):
As matt has asked for details of how I am presenting ViewController, adding screenshot of storyboard segue configuration

Swift free hand drawing

I am trying this code to free hand drawing in UIImageView.
The problem in ( carves ).
class DrawingImageView: UIImageView {
var points: [CGPoint]?
var path: UIBezierPath?
var pathLayer: CAShapeLayer!
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
pathLayer = CAShapeLayer()
pathLayer.fillColor = UIColor.clear.cgColor
pathLayer.strokeColor = UIColor.clear.withAlphaComponent(0.5).cgColor
pathLayer.lineWidth = 15
pathLayer.lineJoin = .round
pathLayer.lineCap = .round
pathLayer.allowsEdgeAntialiasing = true
pathLayer.allowsGroupOpacity = true
pathLayer.shadowColor = UIColor.black.cgColor
pathLayer.shadowOpacity = 1
pathLayer.shadowOffset = CGSize.zero
pathLayer.shadowRadius = 5
self.layer.addSublayer(pathLayer)
if let touch = touches.first {
points = [touch.location(in: self)]
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
if let coalescedTouches = event?.coalescedTouches(for: touch) {
points? += coalescedTouches.map { $0.location(in: self) }
} else {
points?.append(touch.location(in: self))
}
if let predictedTouches = event?.predictedTouches(for: touch) {
let predictedPoints = predictedTouches.map { $0.location(in: self) }
pathLayer.path = UIBezierPath.interpolateHermiteFor(points: points! + predictedPoints, closed: false).cgPath
} else {
pathLayer.path = UIBezierPath.interpolateHermiteFor(points: points!, closed: false).cgPath
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
pathLayer.path = UIBezierPath.interpolateHermiteFor(points: points!, closed: false).cgPath
points?.removeAll()
}
}
I don't know what the problem is ( carve ).
It's not rounded and I use:
pathLayer.lineJoin = .round
pathLayer.lineCap = .round
The result is attached in image:

How to drag certain image in iOS?

Wanted to know how I can drag a image across screen and what code would be used. Tried looking up but only older versions of Swift have answer and no longer work. I want to drag the image, but not place finger on screen and it goes to that spot. Just drag.
Gives me the error:
"Use of undeclared type 'uitouch'"
import UIKit
class DraggableImage: UIImageView {
override func touchesMoved(touches: Set<uitouch>, withEvent event: UIEvent?) {
if let touch = touches.first {
let position = touch.locationInView(superview)
center = CGPointMake(position.x, position.y)
}
}
}
You need to subclass UIImageView and in the init you need to set userInteractionEnabled = true and then override this method override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) well, my code is this:
class DraggableImage: UIImageView {
var localTouchPosition : CGPoint?
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.layer.borderWidth = 1
self.layer.borderColor = UIColor.red.cgColor
self.isUserInteractionEnabled = true
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
self.localTouchPosition = touch?.preciseLocation(in: self)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesMoved(touches, with: event)
let touch = touches.first
guard let location = touch?.location(in: self.superview), let localTouchPosition = self.localTouchPosition else{
return
}
self.frame.origin = CGPoint(x: location.x - localTouchPosition.x, y: location.y - localTouchPosition.y)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
self.localTouchPosition = nil
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func drawRect(rect: CGRect) {
// Drawing code
}
*/
}
This is how it looks
Hope this helps
Create a Nsobject Class for moving View and add following Code
import UIKit
class objectClass: UIImageView, UIGestureRecognizerDelegate {
/*
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
// Drawing code
}
*/
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch: UITouch = touches.first!
self.center = touch.location(in: self.superview)
}
}
in mainViewController make a object of NSobject class
var newView: objectClass = objectClass()
on button Action to add new View
#IBAction func objectAdded(theButton: UIButton!) {
let frame = CGRect(x: 100, y: 100, width: 44, height: 44)
newView = objectClass(frame: frame)
if theButton.titleLabel?.text == "image1" {
newView.image = UIImage(named: "1")
} else if theButton.titleLabel?.text == "image2" {
newView.image = UIImage(named: "2")
}else{
newView.image = UIImage(named: "3")
}
newView.contentMode = .scaleAspectFill
newView.isUserInteractionEnabled = true
self.view .addSubview(newView)
newView.alpha = 0
UIView .animate(withDuration: 0.4) {
self.newView.alpha = 1
}
UIView.animate(withDuration: 0.6, delay: 0, options: .curveEaseOut, animations: { () -> Void in
self.sliderViewBottomLayoutConstraint.constant = self.sliderViewBottomLayoutConstraint.constant - self.sliderViewBottomLayoutConstraint.constant
self.view.layoutIfNeeded()
}, completion: nil)
image1Button.isEnabled = false
image2Button.isEnabled = false
image3Button.isEnabled = false
let pinchGesture: UIPinchGestureRecognizer = UIPinchGestureRecognizer(target: self, action: #selector(ViewController.recognizePinchGesture(sender:)))
pinchGesture.delegate = self
let rotateGesture: UIRotationGestureRecognizer = UIRotationGestureRecognizer(target: self, action: #selector(ViewController.recognizeRotateGesture(sender:)))
let tapGesture: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.RemoveSelectedImageOnTap(sender:)))
tapGesture.numberOfTapsRequired = 2
self.newView.addGestureRecognizer(tapGesture)
self.newView.addGestureRecognizer(pinchGesture)
self.newView.addGestureRecognizer(rotateGesture)
}
func recognizePinchGesture(sender: UIPinchGestureRecognizer) {
sender.view!.transform = sender.view!.transform.scaledBy(x: sender.scale, y: sender.scale)
sender.scale = 1
}
func recognizeRotateGesture(sender: UIRotationGestureRecognizer) {
sender.view!.transform = sender.view!.transform.rotated(by: sender.rotation)
sender.rotation = 0
}

Resources