Bezier path with line width on one side? - ios

I have two images, and I want to have the left one clipped so it looks like they are placed with a diagonal line in between. Like so:
I'm able to create the path and clip, but cannot figure out how to set the line width for just the right part...
My code:
let path = UIBezierPath()
path.moveToPoint(CGPoint(x: 0, y: 0))
path.addLineToPoint(CGPoint(x: leftContentView.frame.width, y: 0))
path.addLineToPoint(CGPoint(x: leftContentView.frame.width - 20, y: leftContentView.frame.height))
path.addLineToPoint(CGPoint(x: 0, y: leftContentView.frame.height))
path.closePath()
let mask = CAShapeLayer()
mask.path = path.CGPath
self.leftContentView.layer.mask = mask

Just use no line and add an additional layer representing the line with the appropriate implied width.

Related

Is it possible to show/stroke only a portion of a UIBezierPath?

I have a UIBezierPath shape (comprised of various line segments) already defined, but depending on the scenario, I only want certain portions of it visible. Is this possible?
I was initially thinking along the lines of how when you animate a UIBezierPath, you can animate from a .fromValue to a .toValue. Can I set some sort of "current value" (which I see exists, but only as a getOnly property) to accomplish something similar, outside of an animation context?
Another way I thought of was to just treat my path as a sort of "template" path, and then break it up into various subpaths, but I'm hoping there is an easier/more direct way (unless this method is easier than I anticipate...).
UIBezierPath doesn't have that, but if you add the path to a CAShapeLayer, you can control the strokeStart and strokeEnd.
Try this in a playground:
let path = UIBezierPath()
path.move(to: .zero)
path.addLine(to: CGPoint(x: 10, y: 10))
path.addLine(to: CGPoint(x: 20, y: 0))
path.addLine(to: CGPoint(x: 30, y: 10))
let layer = CAShapeLayer()
layer.path = path.cgPath
layer.strokeColor = UIColor.red.cgColor
layer.fillColor = UIColor.clear.cgColor
// Note these lines
layer.strokeStart = 0.2 // start at 20% of the way
layer.strokeEnd = 0.8 // end at 80% of the way
layer.lineWidth = 2
let view = UIView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
view.layer.addSublayer(layer)
The above produces:
Without the two lines that set the stroke start and end, the view looks like:

How to add a custom shape to an UIImageView in Swift?

I'm trying to add a custom shape to an imageView. Please check the below images.
This is the required one:
This is what I have done so far:
I'm new to Core Graphics and I have done this so far:
private func customImageClipper(imageV: UIImageView){
let path = UIBezierPath()
let size = imageV.frame.size
print(size)
path.move(to: CGPoint(x: 0.0, y: size.height))
path.addLine(to: CGPoint(x: 0.8, y: size.height/2))
path.close()
let shape = CAShapeLayer()
shape.path = path.cgPath
imageV.layer.sublayers = [shape]
}
I'm creating a function to achieve a shape like this, but whenever I pass the imageView into this function, I can not see any change at all. I know that I have to move from points to another point to achieve this shape, but I have never done this. Any help would be appreciated. This is how I'm calling this function:
imageV.layoutIfNeeded()
customImageClipper(imageV: imageV)
P.S.: I'm not using Storyboard, I have created this programmatically.
There are many ways to create shapes using UIBezierPaths. This post here discusses the use of the draw function to create a shape.
Here is an example using your clip function within the cell.
func clip(imageView: UIView, withOffset offset: CGFloat) {
let path = UIBezierPath()
//Move to Top Left
path.move(to: .init(x: imageView.bounds.size.width * offset, y: 0))
//Draw line from Top Left to Top Right
path.addLine(to: .init(x: imageView.bounds.size.width, y: 0))
//Draw Line from Top Right to Bottom Right
path.addLine(to: .init(x: imageView.bounds.size.width * (1 - offset), y: imageView.bounds.size.height))
//Draw Line from Bottom Right to Bottom Left
path.addLine(to: .init(x: 0, y: imageView.bounds.size.height))
//Close Path
path.close()
//Create the Shape Mask for the ImageView
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.fillColor = UIColor.black.cgColor
imageView.layer.mask = shapeLayer
}
In this function, the offset is the amount of angle you would like on the shape, ranging from 0 to 1. (0.4) seems to work for your requirements.
This shares a lot of similarities with Apseri's answer, except I chose the route of percentages, rather than exact size. Nothing wrong with either approach, I just found it easier to understand with percentages. :)
One last note to point out, I used this function in the layoutSubviews function.
override func layoutSubviews() {
super.layoutSubviews()
imageView.layoutIfNeeded()
clip(imageView: self.imageView, withOffset: 0.4)
}
This output the following image:
Hope this helps.
Here is example of some path clipping. Of course path can be also put via parameters, and this can be applied to any view, as shown.
Before:
After (grey background is below ScrollView background):
func customImageClipper(imageV: UIView){
let path = UIBezierPath()
let size = imageV.frame.size
path.move(to: CGPoint(x: size.width/3.0, y: 0))
path.addLine(to: CGPoint(x: size.width/3.0 + 50, y: 0))
path.addLine(to: CGPoint(x: size.width/3.0, y: size.height))
path.addLine(to: CGPoint(x: size.width/3.0 - 50, y: size.height))
path.addLine(to: CGPoint(x: size.width/3.0, y: 0))
path.close()
let shape = CAShapeLayer()
shape.path = path.cgPath
shape.fillColor = UIColor.black.cgColor
imageV.layer.mask = shape
}
1- Subclassing your UIImageView
2- implement your custom drawings inside setNeedsLayout using UIBezierPath
class MyCustomImageView: UIImageView {
override func setNeedsLayout() {
let path = UIBezierPath()
path.moveToPoint(CGPoint(x: self.frame.size.width/2, y: self.frame.size.height))
path.addLineToPoint(CGPoint(x: self.frame.size.width, y: self.frame.size.height/2))
path.addLineToPoint(CGPoint(x: self.frame.size.width/2, y: 0))
path.addArcWithCenter(CGPoint(x: self.frame.size.width/2, y: self.frame.size.height/2), radius: self.frame.size.width/2, startAngle:-CGFloat(M_PI_2), endAngle: CGFloat(M_PI_2), clockwise: false)
path.moveToPoint(CGPoint(x: self.frame.size.width/2, y: self.frame.size.height))
path.closePath()
UIColor.redColor().setFill()
path.stroke()
path.bezierPathByReversingPath()
let shapeLayer = CAShapeLayer()
shapeLayer.frame = self.bounds
shapeLayer.path = path.CGPath
shapeLayer.fillColor = UIColor.redColor().CGColor
self.layer.mask = shapeLayer;
self.layer.masksToBounds = true;
}
}

How to set shadow at left right and bottom in UIView in ios Swift?

I want shadow at left, right and bottom in view. But Swift 3 shadow offset provide only height or width. I found similar answer see below link it show shadow top left right shadow. I want left right and bottom.
see link little similar answer
It's almost the same task as link provided, just change the BezierPath.
let block1 = UIView(frame: CGRect.init(x: 50, y: 50, width: 300, height: 300))
block1.backgroundColor = UIColor.red
block1.layer.masksToBounds = false
block1.layer.shadowOffset = CGSize(width: 0.0, height: 0.0)
block1.layer.shadowRadius = 5.0
block1.layer.shadowOpacity = 0.7
let path = UIBezierPath()
path.move(to: CGPoint(x: 0.0, y: 0.0))
// Move to center between two top points instead of bottom
path.addLine(to: CGPoint(x: block1.frame.size.width/2.0, y: block1.frame.size.height/2.0))
path.addLine(to: CGPoint(x: block1.frame.size.width, y: 0.0))
path.addLine(to: CGPoint(x: block1.frame.size.width, y: block1.frame.size.height))
path.addLine(to: CGPoint(x: 0.0, y: block1.frame.size.height))
path.close()
block1.layer.shadowPath = path.cgPath

How to make a CGRect slashed at bottom?

I'm trying to implement the following:
A custom NavigationBar which is slashed at the bottom, how can I do this using CGRect?
Another alternative would be to make a image of it and push the list up to overlap the transparent part in the image. I'd prefer the first solution if possible tho. Any suggestions?
This is my current WIP state:
You can use Bezier path
Just add lines and make your custom shape
like this:
let path = UIBezierPath()
shape.move(to: CGPoint(x: 0, y: 0))
shape.addLine(to: CGPoint(x: 0, y: 320))
shape.addLine(to: CGPoint(x: 94, y: 320))
shape.addLine(to: CGPoint(x: 64, y: 0))
shape.close()

Playground code: AffineTransform and AnchorPoint don't work as I want

I want to shrink a triangle downward as below:
But the triangle shrink upward as below:
The transform code is below:
layer.setAffineTransform(CGAffineTransformMakeScale(1.0, 0.5))
I tried to use anchorPoint but I can't scceed.
//: Playground - noun: a place where people can play
import UIKit
let view = UIView(frame: CGRectMake(0, 0, 200, 150))
let layer = CAShapeLayer()
let triangle = UIBezierPath()
triangle.moveToPoint (CGPointMake( 50, 150))
triangle.addLineToPoint(CGPointMake(100, 50))
triangle.addLineToPoint(CGPointMake(150, 150))
triangle.closePath()
layer.path = triangle.CGPath
layer.strokeColor = UIColor.blueColor().CGColor
layer.lineWidth = 3.0
view.layer.addSublayer(layer)
view
layer.setAffineTransform(CGAffineTransformMakeScale(1.0, 0.5))
view
Anish gave me a advice but doesn't work well:
layer.setAffineTransform(CGAffineTransformMakeScale(2.0, 0.5))
Transforms operate around the layer's anchorPoint, which by default is at the center of the layer. Thus, you shrink by collapsing inwards, not by collapsing downwards.
If you want to shrink downward, you will need to scale down and change the view's position (or use a translate transform in addition to your scale transform), or change the layer's anchorPoint before performing the transform.
Another serious problem with your code is that your layer has no assigned size. This causes the results of your transform to be very misleading.
I ran this version of your code and got exactly the results you are asking for. I have put a star next to the key lines that I added. (Note that this is Swift 3; you really need to step up to the plate and update here.)
let view = UIView(frame:CGRect(x: 0, y: 0, width: 200, height: 150))
let layer = CAShapeLayer()
layer.frame = view.layer.bounds // *
let triangle = UIBezierPath()
triangle.move(to: CGPoint(x: 50, y: 150))
triangle.addLine(to: CGPoint(x: 100, y: 50))
triangle.addLine(to: CGPoint(x: 150, y: 150))
triangle.close()
layer.path = triangle.cgPath
layer.strokeColor = UIColor.blue.cgColor
layer.lineWidth = 3
view.layer.addSublayer(layer)
view
layer.anchorPoint = CGPoint(x: 0.5, y: 0) // *
layer.setAffineTransform(CGAffineTransform(scaleX: 1, y: 0.5))
view
Before:
After:

Resources