I'm trying to get this effect in Swift, tried many ways, UIBezierPath and so on, but nothing gets even closer to what I want to achieve. I'm interested in the bottom part of the image, that curve + shadow.
The best result I got with a UIBezierPath that cuts a view I placed on the bottom of the image. But then I can't put the shadow in there. So, any other ideas ? Thanks !
PS: I didn't like the approach I took with my code so I didn't want to post it, but anyway, here it is:
I put a white UIView on top of the image, on the bottom of it and 'cut' through it like this:
let freeform = UIBezierPath()
freeform.move(to: .zero)
freeform.addQuadCurve(to: CGPoint(x: viewArch.width / 2, y: viewArch.height), controlPoint: CGPoint(x: viewArch.width / 4, y: viewArch.height))
freeform.addQuadCurve(to: CGPoint(x: viewArch.width, y: 0), controlPoint: CGPoint(x: viewArch.width * 0.75, y: viewArch.height))
freeform.addLine(to: CGPoint(x: viewArch.width, y: viewArch.height))
freeform.addLine(to: CGPoint(x: 0, y: viewArch.height))
freeform.addLine(to: .zero)
freeform.close()
let maskLayerA = CAShapeLayer()
maskLayerA.path = freeform.cgPath
viewArch.layer.mask = maskLayerA
This results in something like:
But I used the path on my white view, not on the image, so no idea how to put now a shadow.
I outlined the viewArch here, just to be clear:
Related
I have enabled gradient view in attribute inspector but Iām having trouble fixing the colors to show more of a transition with the colors and also show the colors going from bottom to top instead of colors going left to right? What are the modifications on the right pane that need to be made?
You want Start PointX and End PointX to be 0. Then set Start PointY to 0 and End PointY to 1. This will give a gradient from top to bottom starting with the Top Color at the top and the Bottom Color at the bottom.
I wasn't aware you could add gradients via storyboard to be honest, but if you do this via code you'll have a lot more options. Such as more colors, colour locations etc
// Create a gradient layer
let gradient: CAGradientLayer = CAGradientLayer()
// Create an array of colours you can use, as many colours as you require
gradient.colors = [.blue.cgColor, .red.cgColor, .orange.cgColor].cgColor
// Chose the locations for your colors, 0.5 is center
gradient.locations = [0.0, 0.5, 1.0]
// Start and end points so this goes from y: 0.0 to y: 1.0 so top to bottom
gradient.startPoint = CGPoint(x: 1.0, y: 0.0)
gradient.endPoint = CGPoint(x: 1.0, y: 1.0)
// Set the frame
gradient.frame = CGRect(x: 0.0, y: 0.0, width: yourView.frame.size.width, height: yourView.frame.size.height)
// Add to view
yourView.layer.insertSublayer(gradient, at: 0)
I am trying to annotate a PDF using type .ink in my application using a UIBezierPath. I have included a snippet of the pertinent code below (can add the whole sample but issue is only with rotating the path). The issue is when I apply this path, it is rotated 180 degrees around the x- axis so basically it is flipped upside down. I would like to be able to rotate this path 180 degrees around the x-axis so it appears as initially intended. I have seen example of rotating around the z-axis but none around the x-axis. Any help would be greatly appreciated!
let rect = CGRect(x: 110, y: 100, width: 400, height: 300)
let annotation = PDFAnnotation(bounds: rect, forType: .ink, withProperties: nil)
annotation.backgroundColor = .blue
path.apply(CGAffineTransform(scaleX: 0.2, y: 0.2))
annotation.add(path)
// Add annotation to the first page
page.addAnnotation(annotation)
pdfView?.document?.page(at: 0)?.addAnnotation(annotation)
I was actually able to solve this issue using the following scale and a translation transforms:
let rect = CGRect(x: 110, y: 100, width: 400, height: 300)
let annotation = PDFAnnotation(bounds: rect, forType: .ink, withProperties: nil)
annotation.backgroundColor = .blue
// OVER HERE š
path.apply(CGAffineTransform(scaleX: 1.0, y: -1.0))
path.apply(CGAffineTransform(translationX: 0, y: rect.size.height))
annotation.add(path)
// Add annotation to the first page
page.addAnnotation(annotation)
pdfView?.document?.page(at: 0)?.addAnnotation(annotation)
This solution is inspired from the Apple Developer Documentation example.
It was a bit tricky because the PDFKit Coordinate System uses the bottom/left as the origin, with the x- axis going left-to-right and the y- axis going bottom-to-top. That's contrary to the origin: top/left, x: left-to-right and y: top-to-bottom pattern usually encountered on iOS. But this is what we're doing:
CGAffineTransform(scaleX: 1.0, y: -1.0) - scaling the y coordinate to -1.0 makes your path flip 180 degrees around the x- axis (said axis visually being the bottom line of the rect). This means the path is now below of the rect, which might give you the impression that it has disappeared (you won't even find it by Capturing the View Hierarchy since it will show you the entire PDFView as one UIView component, which may or may not drive you insane).
CGAffineTransform(translationX: 0, y: rect.size.height) - now that the path is at the bottom of the rect (but actually at the "top" according to the PDFKit Coordinate System), we need to bring it back into the visible area. Which is why we need to apply a translation transform to move the path up (or down - thanks again PDFKit) into the rect.
Hope this helps! Cheers
I'm using this piece of code to draw a straight line using UIBezierPath class as follows:
let myPath = UIBezierPath()
myPath.move(to: CGPoint(x:10, y:5))
myPath.addLine(to: CGPoint(x:100, y:5))
myPath.close()
UIColor.blue.set()
myPath.stroke()
myPath.fill()
however, I don't know how to change this basic drawing to include half a circle in the path to be as follows:
From: addArc documentation
myPath.addArc(withCenter: CGPoint(x: 55, y: 5),
radius: 10,
startAngle: 0,
endAngle: CGFloat.pi,
clockwise: false)
This should give you about the circle you want. You will also need to break your line into 2 segments.
I have this inside my GameScene which is called in the didMove()
for i in 1...5 {
// path to create the circle
let path = UIBezierPath(arcCenter: CGPoint(x: center.x, y: center.y), radius: CGFloat(((43 * i) + 140)), startAngle: CGFloat(GLKMathDegreesToRadians(-50)), endAngle: CGFloat(M_PI * 2), clockwise: false)
// the inside edge of the circle used for creating its physics body
let innerPath = UIBezierPath(arcCenter: CGPoint(x: center.x, y: center.y), radius: CGFloat(((43 * i) + 130)), startAngle: CGFloat(GLKMathDegreesToRadians(-50)), endAngle: CGFloat(M_PI * 2), clockwise: false)
// create a shape from the path and customize it
let shape = SKShapeNode(path: path.cgPath)
shape.lineWidth = 20
shape.strokeColor = UIColor(red:0.98, green:0.99, blue:0.99, alpha:1.00)
// create a texture and apply it to the sprite
let trackViewTexture = self.view!.texture(from: shape)
let trackViewSprite = SKSpriteNode(texture: trackViewTexture)
trackViewSprite.physicsBody = SKPhysicsBody(edgeChainFrom: innerPath.cgPath)
self.addChild(trackViewSprite)
}
It uses UIBezierPaths to make a few circles. It converts the path into a SKShapeNode then a SKTexture and then applies it to the final SKSpriteNode.
When I do this, the SKSpriteNode is not where it should be, it is a few to the right:
But when I add the SKShapeNode I created, it is set perfectly fine to where it should be:
Even doing this does not center it!
trackViewSprite.position = CGPoint(x: 0, y: 0)
No matter what I try it just will not center.
Why is this happening? Some sort of bug when converting to a texture?
P.S - This has something to do with this also Keep relative positions of SKSpriteNode from SKShapeNode from CGPath
But there is also no response :(
Edit, When I run this:
let testSprite = SKSpriteNode(color: UIColor.yellow, size: trackViewSprite.size)
self.addChild(testSprite)
It shows it has the same frame also:
After a long discussion, we determined that the problem is due to the frame size not being the expected size of the shape.
To combat this, the OP created an outer path of his original path, and calculated the frame that would surround this. Now this approach may not work for everybody.
If anybody else comes across this issue, they will need to do these things:
1) Check the frame of the SKShapeNode to make sure that it is correct
2) Determine what method is best to calculate the correct desired frame
3) Use this new frame when getting textureFromNode to extract only the desired texture size
I've been using CGAffineTransformMakeScale to scale a UIView. This scales it by it's default anchorPoint, which is (.5, .5) -- the center. I'd like to scale it so that it stretches towards the left, as if it were zooming towards the left. I can accomplish this by setting the anchorPoint to the right center. However, this causes problems with setting the frame, etc. Is there a better way to apply a scale transform like I want. I was thinking I could do it with a custom transform matrix, but I am no good with matrix math.
Use anchorPoint, to get around the frame weirdness just store and reload it as in this answer.
First run this code and you will have better understanding. Here lblLabel is my label, you can change accordingly. Also check CGPoint(x: 10, y: 98) and CGPoint(x: 10, y: 108) as par > your requirements.
This will animate from Top Left corner of label. Change anchor point and position as par your requirements.
self.lblLabel.layer.anchorPoint = CGPoint(x: 0.0,y :0.0)
self.lblLabel.layer.position = CGPoint(x: 10, y: 98)
lblLabel.transform = CGAffineTransform(scaleX: 0.5, y: 0.5)
UIView.animate(withDuration: 1.0) {
self.lblLabel.layer.position = CGPoint(x: 10, y: 108)
self.lblLabel.transform = CGAffineTransform(scaleX: 1.2, y: 1.2)
}