iOS Swift 3 - Clearing the UIView - ios

I'm building an iOS app where I have a class named "DrawableCanvas" that extends UIView. The idea is to enable users to draw on UIViews in the Storyboard that extend the class I made.
The drawing works fine and smooth at this point. Here's the code I used:
class DrawableView: UIView {
let path=UIBezierPath()
var previousPoint:CGPoint
var lineWidth:CGFloat=1.0
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override init(frame: CGRect) {
previousPoint=CGPoint.zero
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
previousPoint=CGPoint.zero
super.init(coder: aDecoder)
let panGestureRecognizer=UIPanGestureRecognizer(target: self, action: #selector(pan))
panGestureRecognizer.maximumNumberOfTouches=1
self.addGestureRecognizer(panGestureRecognizer)
}
override func draw(_ rect: CGRect) {
// Drawing code
UIColor.black.setStroke()
path.stroke()
path.lineWidth=lineWidth
}
func pan(panGestureRecognizer:UIPanGestureRecognizer)->Void{
let currentPoint=panGestureRecognizer.location(in: self)
let midPoint=self.midPoint(p0: previousPoint, p1: currentPoint)
if panGestureRecognizer.state == .began{
path.move(to: currentPoint)
}
else if panGestureRecognizer.state == .changed{
path.addQuadCurve(to: midPoint,controlPoint: previousPoint)
}
previousPoint=currentPoint
self.setNeedsDisplay()
}
func midPoint(p0:CGPoint, p1:CGPoint)->CGPoint{
let x=(p0.x+p1.x)/2
let y=(p0.y+p1.y)/2
return CGPoint(x: x, y: y)
}
}
Of course, I have to clear the view on a button press, and here's my button press action:
#IBAction func reset1(_ sender: AnyObject) {
print("reset hit!")
sig1View.setNeedsLayout()
sig1View.setNeedsDisplay()
}
where sig1View is the view I'm trying to reset.
This didn't work out, so what I tried instead was to write a reloadData() in the DrawableView class:
func reloadData() {
print("reload data hit")
self.setNeedsDisplay()
}
The function got called, but that too, didn't work. How do I clear or reset a UIView in iOS Swift 3?

Call your DrawableView's (instance's) path.removeAllPoints function to remove all the drawings/path.
So following your code, reset1 should look like this now:
#IBAction func reset1(_ sender: AnyObject) {
print("reset hit!")
sig1View.path.removeAllPoints()
sig1View.setNeedsDisplay()
}

The problem is that, although your question talks about "resetting", your code does not in fact reset anything. You are saying:
override func draw(_ rect: CGRect) {
// Drawing code
UIColor.black.setStroke()
path.stroke()
path.lineWidth=lineWidth
}
So that's what's happening. As long as path contains a drawable path, that is what is drawn in your view whenever the view redraws itself. If that's not what you want, change the path, or change the implementation of draw.

Add this method to DrawableView class and call it with sig1View instance:
func clear()
{
path.removeAllPoints()
setNeedsDisplay()
}
and call it from the reset method.
Usage:
sig1View.clear()

Related

Start CAAnimation when view is not in view hierarchy

I have a UI component (a loading spinner) that has an endless animation:
class MySpinner: UIView {
...
override var bounds: CGRect {
didSet {
updateLayers()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
self.layer.addSublayer(self.animatedLayer)
self.updateLayers()
self.startAnimating()
}
private func updateLayers() {
//Set up path, stroke color and such
self.animatedLayer.frame = self.bounds
}
private func startAnimating() {
CATransaction.begin()
CATransaction.setDisableActions(true) //disable automatic animations by iOS
let endlessRotationAnimation = CAKeyframeAnimation(keyPath: "transform.rotation")
endlessRotationAnimation.values = [ 0.0, CGFloat(2.0 * .pi) ]
endlessRotationAnimation.keyTimes = [ 0.0, 1.0 ]
endlessRotationAnimation.repeatDuration = .infinity
endlessRotationAnimation.duration = 1.0
self.animatedLayer.add(endlessRotationAnimation, forKey: "rotationAnimation")
CATransaction.commit()
}
}
It turns out this is not working. I assume that if I call startAnimating() before the view is added to the window, iOS will immediately remove the animation. If I first add the view to the view hierarchy and then call startAnimating(), things start working.
Is there a way to prevent iOS removing my animation? Or is there some documentation about this behaviour, I couldn't find any?
I was able to work around this issue by overriding didMoveToWindow.
public override func didMoveToWindow() {
super.didMoveToWindow()
startAnimating()
}

iOS charts - draw value only when highlighted

I want to highlight and show value only when tapping on the iOS-Chart. I enabled the highlight but not the values because I only want them when tap and highlight
lineChartDataSet.drawValuesEnabled = false
lineChartDataSet.highlightEnabled = true
Do I need this function?
func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) {}
It's an old question, but I think it's still actual for some developers.
If you want to show values, baloon or highlight bar only while user is touching a chart view you may catch a touch event with UILongPressGestureRecognizer.
I instantiated new TappableLineChartView class from LineChartView. But you can work with BarChartView in the same way. Also if you don't want to instantiate new classes, you can incorporate addTapRecognizer and chartTapped functions in your view controller.
In my example, I show and hide values, but in the same manner, you can show and hide a balloon or another marker.
class TappableLineChartView: LineChartView {
public override init(frame: CGRect)
{
super.init(frame: frame)
addTapRecognizer()
}
public required init?(coder aDecoder: NSCoder)
{
super.init(coder: aDecoder)
addTapRecognizer()
}
func addTapRecognizer() {
let tapRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(chartTapped))
tapRecognizer.minimumPressDuration = 0.1
self.addGestureRecognizer(tapRecognizer)
}
#objc func chartTapped(_ sender: UITapGestureRecognizer) {
if sender.state == .began || sender.state == .changed {
// show
let position = sender.location(in: self)
let highlight = self.getHighlightByTouchPoint(position)
let dataSet = self.getDataSetByTouchPoint(point: position)
dataSet?.drawValuesEnabled = true
highlightValue(highlight)
} else {
// hide
data?.dataSets.forEach{ $0.drawValuesEnabled = false }
highlightValue(nil)
}
}
}

touchesBegan and touchesMoved not getting triggered on UIView

What I'm trying to achieve:
Trying to grab the coordinate of the currently touched area on the screen and draw the received coordinate on the screen. Simply put, a basic drawing app that's all written programmatically (for my own practice).
Problem
touchesBegan and touchesMoved for PaintingSubclass are not getting called at all.
Current setup
I have a PaintingViewController then I also have a PaintingSubclass.
PaintingViewController is a UIViewController and PaintingSubclass is a UIView.
PaintingViewController creates an instance of PaintingSubclass and adds it to the subview of PaintingViewController.
PaintingSubclass is where the actual drawing happens.
What I've tried so far
Put a breakpoint inside the touchesMoved and touchesBegan (didn't work)
Tried to add a UITapGestureRecognizer (didn't work)
Enabled self.isUserInteractionEnabled = true (didn't work)
Make PaintingSubclass inherit UIControl and call sendActions(for: .valueChanged) at the end of touchesMoved and touchesBegan (didn't work)
Current Code
(please ignore any the unnecessary variables)
import UIKit
class PaintingViewController: UIViewController{
var _paintView: PaintingSubclass? = nil
override func loadView() {
view = UILabel()
}
private var labelView: UILabel {
return view as! UILabel
}
override func viewDidLoad() {
_paintView = PaintingSubclass()
_paintView?.frame = CGRect(x: view.bounds.minX, y: view.bounds.minY, width: 400, height: 750)
_paintView?.backgroundColor = UIColor.white
_paintView?.setNeedsDisplay()
view.addSubview(_paintView!)
}
class PaintingSubclass: UIView{
private var context: CGContext? = nil
var styleSelection: String = "buttcap"
var linewidth: Float = 5
var lineCapStyle: CGLineCap? = nil
var lineJoinStyle: CGLineJoin? = nil
var lineWidthValue: CGFloat? = nil
var colorValue: UIColor? = nil
override init(frame: CGRect) {
super.init(frame: frame)
lineWidthValue = 0.5
colorValue = UIColor(cgColor: UIColor.black.cgColor)
self.isUserInteractionEnabled = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func draw(_ rect: CGRect) {
context = UIGraphicsGetCurrentContext()!
context?.move(to: CGPoint(x: 0.0, y: 110.0))
context?.setStrokeColor((colorValue?.cgColor)!)
context?.drawPath(using: CGPathDrawingMode.stroke)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
let touch: UITouch = touches.first!
let touchPoint: CGPoint = touch.location(in: self)
let _xValue = touchPoint.x
let _yValue = touchPoint.y
NSLog("coordinate: \(_xValue), \(_yValue)")
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesMoved(touches, with: event)
let touch: UITouch = touches.first!
let touchPoint: CGPoint = touch.location(in: self)
let _xValue = touchPoint.x
let _yValue = touchPoint.y
NSLog("coordinate: \(_xValue), \(_yValue)")
}
}
}
What am I doing wrong here? Been stuck in this state for hours not making any progress. Any help/feedbacks will be much appreciated.
I don't know why you are setting your default view to UILabel(), but your default isUserInteractionEnabled of your view is false. Set it to true
override func loadView() {
view = UILabel()
view.isUserInteractionEnabled = true
}
According to your code, your PaintingViewController's view is a UILabel, the property isUserInteractionEnabled default is false.Try to set this property to true may help you.
The problem lies on:
// override func loadView() {
// view = UILabel()
// }
//
// private var labelView: UILabel {
// return view as! UILabel
// }
You turn the view controller's self view object to a UILabel object.
It would be better to have your painting view as the main view in the view controller and the UILabel as a subview of the painting view. The UILabel needs to have userInteractionEnabled in your current layout, but instead of changing that switch the layout around.
Apart from anything else, presumably you don't want the painting view to draw over the label, but rather the label to be drawn on top, so the label should be the subview.

Custom UIView with tapGesture

I have a drawing app. Inside my VC there are five imageViews with five colors in them. I want to be able to click on the imageView and change the stroke color. It can be easily done if I repeat myself in the viewcontroller by adding gesture Recognizers to each UIImageView and have their individual "selector" function. Such as
func redTapped() {}
func blueTapped() {}
However, I want to be able to make the code more clear by creating a custom class (ColorImageView.Swift) for these ImageViews so that when I assign the class to these buttons, they automatically gets the tap gesture and my VC automatically receives the information about which one is tapped. At the moment, I can get a "imagePressed" printed out for each image that gets assigned to my class. However, I have no way of distinguishing which one were pressed. Below are my code for ColorImageView.Swift
import Foundation
class ColorImageView: UIImageView {
private func initialize() {
let touchGesture = UITapGestureRecognizer(target: self, action: #selector(ColorImageView.imagePressed(_:)))
touchGesture.numberOfTapsRequired = 1
self.userInteractionEnabled = true
self.addGestureRecognizer(touchGesture)
}
override init(frame: CGRect) {
super.init(frame: frame)
initialize()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initialize()
}
func imagePressed(gestureRecognizer: UITapGestureRecognizer) {
print("image pressed \(gestureRecognizer)")
}
}
My imageView names are red.png, green.png, blue.png...etc
Thanks
You can get the tag easily.It works fine.
func imagePressed(gestureRecognizer: UITapGestureRecognizer)
{
print("image pressed \(gestureRecognizer)")
let tappedImageVIew = gestureRecognizer.view as! UIImageView
print("image pressed \(tappedImageVIew.tag)")
}

Why can't I draw on an image when I subclass UIView?

I am trying to draw on an image from the camera roll using the code from this tutorial. After selecting a photo from the camera roll, imageView is set to the photo. When the user presses the draw button, tempImageView is added over the photo, so the user can draw onto it. However, when I attempt to draw on the image, nothing happens. Why?
Code:
class EditViewController: UIViewController{
var tempImageView: DrawableView!
var imageView: UIImageView = UIImageView(image: UIImage(named: "ClearImage"))
func draw(sender: UIButton!) {
tempImageView = DrawableView(frame: imageView.bounds)
tempImageView.backgroundColor = UIColor.clearColor()
self.view.addSubview(tempImageView)
}
}
class DrawableView: UIView {
let path=UIBezierPath()
var previousPoint:CGPoint
var lineWidth:CGFloat=10.0
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override init(frame: CGRect) {
previousPoint=CGPoint.zero
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
previousPoint=CGPoint.zero
super.init(coder: aDecoder)
let panGestureRecognizer=UIPanGestureRecognizer(target: self, action: "pan:")
panGestureRecognizer.maximumNumberOfTouches=1
self.addGestureRecognizer(panGestureRecognizer)
}
override func drawRect(rect: CGRect) {
// Drawing code
UIColor.greenColor().setStroke()
path.stroke()
path.lineWidth=lineWidth
}
func pan(panGestureRecognizer:UIPanGestureRecognizer)->Void
{
let currentPoint=panGestureRecognizer.locationInView(self)
let midPoint=self.midPoint(previousPoint, p1: currentPoint)
if panGestureRecognizer.state == .Began
{
path.moveToPoint(currentPoint)
}
else if panGestureRecognizer.state == .Changed
{
path.addQuadCurveToPoint(midPoint,controlPoint: previousPoint)
}
previousPoint=currentPoint
self.setNeedsDisplay()
}
func midPoint(p0:CGPoint,p1:CGPoint)->CGPoint
{
let x=(p0.x+p1.x)/2
let y=(p0.y+p1.y)/2
return CGPoint(x: x, y: y)
}
}
make sure that tempImageView.userIteractionEnabled = YES. by default this property for UIImageView is NO.
after put breakpoint to you gestureRecogniser method, see if it's being called.

Resources