BezierPath in Swift Using Playgrounds - ios

Do I have to create a custom class in order to use the BezierPath in the Swift playgrounds?
The following code displays nothing but black background:
import UIKit
import XCPlayground
class GraphView : UIView {
override func drawRect(rect: CGRect) {
let path = UIBezierPath(rect: rect)
path.moveToPoint(CGPointMake(0,0))
path.addLineToPoint(CGPointMake(50,100))
path.closePath()
UIColor.redColor().setFill()
path.stroke()
}
}
let graphView = GraphView(frame: CGRectMake(0,0,960,640))

You have to use this at the end of your code:
XCPlaygroundPage.currentPage.liveView = graphView
and to open the Playground's "Assistant Editor" to see the result.
And also to change the background color of the view since it's black... and your line is also black. ;)

In Swift 5:
import UIKit
class GraphView : UIView {
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .white
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func draw(_ rect: CGRect) {
let path = UIBezierPath(rect: rect)
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: 50, y: 100))
path.stroke()
}
}
let graphView = GraphView(frame: CGRect(x: 0, y: 0, width: 960, height: 640))
Swift 5.2, Xcode 11.4

Related

How can i see the drawing in storyboard?

I drawed triangle but I can't see on storyboard. How can I see on storyboard this drawing ?
View Controller Code:
class ViewController: UIViewController {
#IBOutlet weak var fooView: UIView!
var polygonShape = CAShapeLayer()
override func viewDidLoad() {
super.viewDidLoad()
let polygonPath = UIBezierPath()
polygonPath.move(to: CGPoint(x: 50, y: -1))
polygonPath.addLine(to: CGPoint(x: 93.3, y: 74))
polygonPath.addLine(to: CGPoint(x: 6.7, y: 74))
polygonPath.close()
polygonShape.frame = fooView.bounds
polygonShape.path = polygonPath.cgPath
fooView.layer.mask = polygonShape
}
}
Change your class to this:
#IBDesignable
class FirstImageView: UIView {
var polygonShape: CAShapeLayer!
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
if polygonShape == nil {
polygonShape = CAShapeLayer()
layer.mask = polygonShape
}
}
override func layoutSubviews() {
super.layoutSubviews()
let polygonPath = UIBezierPath()
polygonPath.move(to: CGPoint(x: 0.0, y: bounds.size.height))
polygonPath.addLine(to: CGPoint(x: bounds.width * 0.5, y: 0.0))
polygonPath.addLine(to: CGPoint(x: bounds.size.width, y: bounds.size.height))
polygonPath.close()
polygonShape.path = polygonPath.cgPath
}
}
You can remove everything from your ViewController class. You don't even need the class at all at this point.
Select the view on your Storyboard, and at the top of the Identity Inspector pane change the Custom Class from the default UIView to FirstImageView.
Under the top Editor menu, either select Refresh All Views or make sure Automatically Refresh Views is checked.
Result:

How to cut one edge of UIView in swift?

I am trying to cut one edge of UIView using Bezier path but i am unable to do it, could anyone please enlighten me ?
Thank You
You can try to assign this to the view
class RightView : UIView {
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func draw(_ rect: CGRect) {
guard let context = UIGraphicsGetCurrentContext() else { return }
context.beginPath()
context.move(to: CGPoint(x: rect.maxX, y: rect.minY))
context.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
context.addLine(to: CGPoint(x:rect.maxX - 50, y: rect.maxY))
context.closePath()
context.setFillColor(UIColor.white.cgColor)
context.fillPath()
}
override func awakeFromNib() {
super.awakeFromNib()
self.layer.borderColor = UIColor.black.cgColor
self.layer.borderWidth = 1
}
}

How Do i create UIView like in the image?

i want to add curve in my UIView as shown in image.
How can i create such uiview?
You can use UIBezierPath and use addCurve method to create your view.
//1. Create this new Class
class ComplexView: UIView {
var path: UIBezierPath!
override init(frame: CGRect) {
super.init(frame: frame)
self.alpha = 0.3
complexShape()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func draw(_ rect: CGRect) {
// Specify the fill color and apply it to the path.
UIColor.blue.setFill()
path.fill()
// Specify a border (stroke) color.
UIColor.magenta.setStroke()
path.stroke()
}
func complexShape() {
path = UIBezierPath()
path.move(to: CGPoint(x: 0.0, y: 0.0))
path.addCurve(to: CGPoint(x: 0, y: self.frame.size.height),
controlPoint1: CGPoint(x: 50.0, y: 25.0),
controlPoint2: CGPoint(x: 50.0, y: self.frame.size.height - 25.0))
path.close()
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
self.backgroundColor = UIColor.orange
self.layer.mask = shapeLayer
}
}
In your ViewController call this View and add this view to your main view.
//2. In you Viewcontoller add ComplexView
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let width: CGFloat = 100.0
let height: CGFloat = 500.0
let complexView = ComplexView(frame: CGRect(x: 0,
y: self.view.frame.size.height/2 - height/2,
width: width,
height: height))
self.view.addSubview(complexView)
}
You need to play around addCurve method to get your desired shape.

Problems rotating UIView with drawing inside

I want to rotate a UIView with a drawing inside (circle, rectangle or line). The problem is when I rotate the view and refresh the drawing (I need ir when I change some properties of the drawing, i.e) the drawing doesn't follow the view...
UIView without rotation:
UIView with rotation:
Here is my simple code (little extract from the original code):
Main ViewController
class TestRotation: UIViewController {
// MARK: - Properties
var drawingView:DrawingView?
// MARK: - Actions
#IBAction func actionSliderRotation(_ sender: UISlider) {
let degrees = sender.value
let radians = CGFloat(degrees * Float.pi / 180)
drawingView?.transform = CGAffineTransform(rotationAngle: radians)
drawingView?.setNeedsDisplay()
}
// MARK: - Init
override func viewDidLoad() {
super.viewDidLoad()
drawingView = DrawingView(frame:CGRect(x:50, y:50, width: 100, height:75))
self.view.addSubview(drawingView!)
drawingView?.setNeedsDisplay()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
UIView
class DrawingView: UIView {
override func draw(_ rect: CGRect) {
let start = CGPoint(x: 0, y: 0)
let end = CGPoint(x: self.frame.size.width, y: self.frame.size.height)
drawCicrle(start: start, end: end)
}
func drawCicrle(start:CGPoint, end:CGPoint) {
//Contexto
let context = UIGraphicsGetCurrentContext()
if context != nil {
//Ancho
context!.setLineWidth(2)
//Color
context!.setStrokeColor(UIColor.black.cgColor)
context!.setFillColor(UIColor.orange.cgColor)
let rect = CGRect(origin: start, size: CGSize(width: end.x-start.x, height: end.y-start.y))
let path = UIBezierPath(ovalIn: rect)
path.lineWidth = 2
path.fill()
path.stroke()
}
}
// MARK: - Init
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.gray
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
It seems that the problem is temporally solved if I don't refresh the view after rotating, but if I have to refresh later for some other reason (properties changed), the problem appears again.
What can I do? Thanks in advance
You are using the frame when you should be using the bounds.
The frame is the rectangle which contains the view. It is specified in the coordinate system of the superview of the view, and the frame changes when you rotate the view (because a rotated square extends further in the X and Y directions than a non-rotated square).
The bounds is the rectangle which contains the contents of the view. It is specified in the coordinate system of the view itself, and it doesn't change as the view is rotated or moved.
If you change frame to bounds in your draw(_:) function, it will work as you expect:
override func draw(_ rect: CGRect) {
let start = CGPoint(x: 0, y: 0)
let end = CGPoint(x: self.bounds.size.width, y: self.bounds.size.height)
drawCicrle(start: start, end: end)
}
Here's a demo showing the oval being redrawn as the slider moves.
Here's the changed code:
class DrawingView: UIView {
var fillColor = UIColor.orange {
didSet {
setNeedsDisplay()
}
}
override func draw(_ rect: CGRect) {
let start = CGPoint(x: 0, y: 0)
let end = CGPoint(x: self.bounds.size.width, y: self.bounds.size.height)
drawCircle(start: start, end: end)
}
func drawCircle(start:CGPoint, end:CGPoint) {
//Contexto
let context = UIGraphicsGetCurrentContext()
if context != nil {
//Ancho
context!.setLineWidth(2)
//Color
context!.setStrokeColor(UIColor.black.cgColor)
context!.setFillColor(fillColor.cgColor)
let rect = CGRect(origin: start, size: CGSize(width: end.x-start.x, height: end.y-start.y))
let path = UIBezierPath(ovalIn: rect)
path.lineWidth = 2
path.fill()
path.stroke()
}
}
// MARK: - Init
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.gray
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
#IBAction func actionSliderRotation(_ sender: UISlider) {
let degrees = sender.value
let radians = CGFloat(degrees * Float.pi / 180)
drawingView?.transform = CGAffineTransform(rotationAngle: radians)
drawingView?.fillColor = UIColor(hue: CGFloat(degrees)/360.0, saturation: 1.0, brightness: 1.0, alpha: 1.0)
}

Frame a UIView with transparent region

I have put a normal UIView in IB in my UIViewController, and set the view to a UIView subclass with the following draw method:
override func draw(_ rect: CGRect) {
super.draw(rect)
self.isOpaque = false
self.backgroundColor?.setFill()
UIRectFill(rect)
let insetRect = self.bounds.insetBy(dx: 0, dy: 4)
UIColor.clear.setFill()
UIRectFill(insetRect)
}
The expected result should be that the background color (white) is shown as a line on top and bottom of the view. That indeed is what happens. But I would like the inset rectangle to be transparent, that is why I set the color to clear. But the result is black instead. See the image.
I found another way of doing it, subclassing UIView, and this time I get the expected transparency in the middle:
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.clear
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func draw(_ rect: CGRect) {
super.draw(rect)
let top = UIBezierPath(rect: CGRect(x: 0, y: 0, width: self.bounds.width, height: 4))
UIColor.white.setFill()
top.fill()
let bottom = UIBezierPath(rect: CGRect(x: 0, y: self.bounds.height - 4, width: self.bounds.width, height: 4))
bottom.fill()
}

Resources