I am experimenting with PaintCode. As an example, I've imported an abstract Illustrator drawing file, which has produced the below bezier Swift code.
On my storyboard I have an UIImageView named exampleImageView that I want to display the PaintCode code and have the ability to scale the resulting image like a vector graphic at different sizes, e.g. 1x, 2.5x 5x etc (i.e. smooth non pixellated lines and curves).
Questions:
1 - How do I display the below code as an image in an UIImageView named exampleImageView in Xcode?
2 - How do I display the below code in an UIView named exampleView in Xcode?
3 - Is it possible to scale up or down the below code (i.e. like a vector graphic), and if so, how in Xcode?
Expected image:
PaintCode code:
//// General Declarations
let context = UIGraphicsGetCurrentContext()
//// Color Declarations
let fillColor = UIColor(red: 0.087, green: 0.086, blue: 0.083, alpha: 1.000)
//// Group 2
CGContextSaveGState(context)
CGContextSetAlpha(context, 0.75)
CGContextBeginTransparencyLayer(context, nil)
//// Bezier Drawing
let bezierPath = UIBezierPath()
bezierPath.moveToPoint(CGPointMake(26.08, 23.65))
bezierPath.addCurveToPoint(CGPointMake(61.71, 12.5), controlPoint1: CGPointMake(38.43, 23.65), controlPoint2: CGPointMake(54.42, 18.61))
bezierPath.addCurveToPoint(CGPointMake(52.65, 1.35), controlPoint1: CGPointMake(69, 6.38), controlPoint2: CGPointMake(64.99, 1.35))
bezierPath.addCurveToPoint(CGPointMake(16.91, 12.5), controlPoint1: CGPointMake(40.2, 1.35), controlPoint2: CGPointMake(24.2, 6.38))
bezierPath.addCurveToPoint(CGPointMake(26.08, 23.65), controlPoint1: CGPointMake(9.63, 18.61), controlPoint2: CGPointMake(13.63, 23.65))
bezierPath.closePath()
bezierPath.moveToPoint(CGPointMake(48.14, 25))
bezierPath.addLineToPoint(CGPointMake(0.79, 25))
bezierPath.addCurveToPoint(CGPointMake(0.24, 24.35), controlPoint1: CGPointMake(-0.02, 25), controlPoint2: CGPointMake(-0.21, 24.73))
bezierPath.addCurveToPoint(CGPointMake(2.41, 23.65), controlPoint1: CGPointMake(0.76, 23.92), controlPoint2: CGPointMake(1.59, 23.65))
bezierPath.moveToPoint(CGPointMake(14.36, 12.5))
bezierPath.addCurveToPoint(CGPointMake(54.26, 0), controlPoint1: CGPointMake(22.62, 5.57), controlPoint2: CGPointMake(40.59, 0))
bezierPath.addCurveToPoint(CGPointMake(64.36, 12.5), controlPoint1: CGPointMake(67.93, 0), controlPoint2: CGPointMake(72.62, 5.57))
bezierPath.addCurveToPoint(CGPointMake(37.3, 23.65), controlPoint1: CGPointMake(58.56, 17.37), controlPoint2: CGPointMake(47.81, 21.59))
bezierPath.addLineToPoint(CGPointMake(49.75, 23.65))
fillColor.setFill()
bezierPath.fill()
CGContextEndTransparencyLayer(context)
CGContextRestoreGState(context)
1 - How do I display the below code as an image in an UIImageView named exampleImageView?
Image can be created in PaintCode, but I don't think it is the right fit here. For starters create a drawing method in PaintCode.
2 - Is it possible to scale up or down the below code (i.e. like a vector graphic), and if so, how?
In PaintCode you can make the thing resizable by using frames.
Look at the Dynamic Shapes tutorial that addresses both.
Related
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:
I want to create an animation where an object animates its position along a path. The trouble I am having is making this path dynamic meaning that it works on all screen sizes.
The spaceship in the gif above follows a "U" shape path initially.
I use this "U" shape path and it works, here is the code:
func animateAlongPath() {
// Create and add image to view
let myImage = UIImage(named: "myImage")!
let myImageView = UIImageView(image: myImage)
myImageView.frame = CGRect(x: 100, y: 100, width: myImage.size.width, height: myImage.size.height)
holderView.addSubview(myImageView)
// Create "U" shape path for animation (path code created in PaintCode)
let path = UIBezierPath()
path.moveToPoint(CGPointMake(59.5, 81.5))
path.addCurveToPoint(CGPointMake(66.5, 91.5), controlPoint1: CGPointMake(63.64, 84.13), controlPoint2: CGPointMake(62.62, 85.9))
path.addCurveToPoint(CGPointMake(81.5, 110.5), controlPoint1: CGPointMake(72.31, 99.88), controlPoint2: CGPointMake(75.91, 103.08))
path.addCurveToPoint(CGPointMake(109.5, 142.5), controlPoint1: CGPointMake(88.44, 119.71), controlPoint2: CGPointMake(102.39, 135.23))
path.addCurveToPoint(CGPointMake(145.5, 156.5), controlPoint1: CGPointMake(122.47, 155.76), controlPoint2: CGPointMake(129.15, 157.54))
path.addCurveToPoint(CGPointMake(237.5, 81.5), controlPoint1: CGPointMake(185.04, 153.99), controlPoint2: CGPointMake(237.5, 81.5))
let pathAnimation = CAKeyframeAnimation(keyPath: "position")
pathAnimation.path = path.CGPath // Use "U" shape path for animation
pathAnimation.calculationMode = kCAAnimationPaced
pathAnimation.timingFunctions = [CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)]
pathAnimation.duration = 1
pathAnimation.removedOnCompletion = false
pathAnimation.fillMode = kCAFillModeForwards
myImageView.layer.addAnimation(pathAnimation, forKey: nil)
myImageView.layer.position = path.currentPoint
}
The problem is that the UIBezierPath is not dynamic. It works great on the iPhone 6 device screen size, but does not work great on any other device size since it is static. I tried using PaintCode to create dynamic paths with variables, but just can't get it to work.
Any ideas?
PaintCode can generate code for resizable UIBezierPaths using Frames.
Put the Bezier inside a Frame object and then set desired autoresizing springs (all flexible). The resulting code will be parametrized with CGRect/NSRect and will use relative coefficients to calculate bezier points.
There’s also a video tutorial about Dynamic Shapes if you want to get more into depth.
I would like to get something like this:
I guess one way to do it would be to draw my rectangle with the two rounded angle in a subpath. Then add another subpass to draw the arrow.
My question is: is there a simpler way to draw the arrow than drawing 4 lines and some curves? I tried doing it by using 2 lines and bezierPath.lineJoinStyle = kCGLineJoinRound but when I fill it, I get a triangle.
This is the code I get from PaintCode when I draw the path.
//// Rectangle Drawing
var rectanglePath = UIBezierPath()
rectanglePath.moveToPoint(CGPointMake(26.71, 14))
rectanglePath.addLineToPoint(CGPointMake(85.29, 14))
rectanglePath.addCurveToPoint(CGPointMake(90.18, 14.39), controlPoint1: CGPointMake(87.8, 14), controlPoint2: CGPointMake(89.05, 14))
rectanglePath.addLineToPoint(CGPointMake(90.4, 14.45))
rectanglePath.addCurveToPoint(CGPointMake(93.57, 17.79), controlPoint1: CGPointMake(91.87, 15.01), controlPoint2: CGPointMake(93.04, 16.24))
rectanglePath.addCurveToPoint(CGPointMake(94, 23.17), controlPoint1: CGPointMake(94, 19.21), controlPoint2: CGPointMake(94, 20.53))
rectanglePath.addLineToPoint(CGPointMake(94, 54))
rectanglePath.addLineToPoint(CGPointMake(18, 54))
rectanglePath.addLineToPoint(CGPointMake(18, 23.17))
rectanglePath.addCurveToPoint(CGPointMake(18.37, 18.02), controlPoint1: CGPointMake(18, 20.53), controlPoint2: CGPointMake(18, 19.21))
rectanglePath.addLineToPoint(CGPointMake(18.43, 17.79))
rectanglePath.addCurveToPoint(CGPointMake(21.6, 14.45), controlPoint1: CGPointMake(18.96, 16.24), controlPoint2: CGPointMake(20.13, 15.01))
rectanglePath.addCurveToPoint(CGPointMake(26.71, 14), controlPoint1: CGPointMake(22.95, 14), controlPoint2: CGPointMake(24.2, 14))
rectanglePath.closePath()
UIColor.grayColor().setFill()
rectanglePath.fill()
//// Bezier Drawing
var bezierPath = UIBezierPath()
bezierPath.moveToPoint(CGPointMake(31.5, 37.5))
bezierPath.addLineToPoint(CGPointMake(56.5, 30.5))
bezierPath.addLineToPoint(CGPointMake(80.5, 37.5))
bezierPath.addLineToPoint(CGPointMake(80.5, 37.5))
bezierPath.addLineToPoint(CGPointMake(56.5, 30.5))
bezierPath.addLineToPoint(CGPointMake(31.5, 37.5))
bezierPath.addLineToPoint(CGPointMake(31.5, 37.5))
bezierPath.closePath()
bezierPath.lineJoinStyle = kCGLineJoinRound;
UIColor.grayColor().setFill()
bezierPath.fill()
UIColor.whiteColor().setStroke()
bezierPath.lineWidth = 5
bezierPath.stroke()
UPDATE:
The arrow is actually some "void" drawing. It's the shape of an arrow but there is nothing inside (we can see through it)
Here's some Core Graphics code that makes a shape like what you're looking for. You would have to translate it into the equivalent BezierPath commands, but that shouldn't be too difficult. You'll of course also have to tweak the coordinates and the colors to your preferences for the size and color. As you can see, it consists of two line shapes with the CGContextSetLineCap command used to round out the ends of each the two line shapes:
CGContextRef ctx = UIGraphicsGetCurrentContext(); // iOS
/* Line Shape 1 */
CGMutablePathRef pathRef = CGPathCreateMutable();
CGPathMoveToPoint(pathRef, NULL, 137, 192);
CGPathAddLineToPoint(pathRef, NULL, 300, 145);
CGContextSetLineWidth(ctx, 30);
CGContextSetLineCap(ctx, kCGLineCapRound);
CGContextSetRGBStrokeColor(ctx, 0.129, 0.992, 1, 1);
CGContextAddPath(ctx, pathRef);
CGContextStrokePath(ctx);
CGPathRelease(pathRef);
/* Line Shape 2 */
CGMutablePathRef pathRef2 = CGPathCreateMutable();
CGPathMoveToPoint(pathRef2, NULL, 463, 192);
CGPathAddLineToPoint(pathRef2, NULL, 300, 145);
CGContextSetLineWidth(ctx, 30);
CGContextSetLineCap(ctx, kCGLineCapRound);
CGContextSetRGBStrokeColor(ctx, 0.129, 0.992, 1, 1);
CGContextAddPath(ctx, pathRef2);
CGContextStrokePath(ctx);
CGPathRelease(pathRef2);
I have the following code and have declared IBDesignable.
The DrawRect is shown perfectly well in Interface Builder, but not at all in the Simulator or on Device.
Does anybody have any ideas why?
import UIKit
import QuartzCore
#IBDesignable class VideoButton: UIButton {
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func drawRect(rect: CGRect)
{
let color2 = UIColor(red: 0.500, green: 0.500, blue: 0.500, alpha: 0.000)
//// Oval Drawing
var ovalPath = UIBezierPath(ovalInRect: CGRectMake(frame.minX + floor(frame.width * 0.03571) + 0.5, frame.minY + floor(frame.height * 0.03571) + 0.5, floor(frame.width * 0.96429) - floor(frame.width * 0.03571), floor(frame.height * 0.96429) - floor(frame.height * 0.03571)))
color2.setFill()
ovalPath.fill()
UIColor.whiteColor().setStroke()
ovalPath.lineWidth = 3
ovalPath.stroke()
//// Rectangle Drawing
let rectanglePath = UIBezierPath(roundedRect: CGRectMake(frame.minX + floor(frame.width * 0.25714 + 0.5), frame.minY + floor(frame.height * 0.34286 + 0.5), floor(frame.width * 0.60000 + 0.5) - floor(frame.width * 0.25714 + 0.5), floor(frame.height * 0.64286 + 0.5) - floor(frame.height * 0.34286 + 0.5)), cornerRadius: 2)
color2.setFill()
rectanglePath.fill()
UIColor.whiteColor().setStroke()
rectanglePath.lineWidth = 3
rectanglePath.stroke()
//// Bezier Drawing
var bezierPath = UIBezierPath()
bezierPath.moveToPoint(CGPointMake(frame.minX + 0.61429 * frame.width, frame.minY + 0.50536 * frame.height))
bezierPath.addLineToPoint(CGPointMake(frame.minX + 0.75714 * frame.width, frame.minY + 0.34286 * frame.height))
bezierPath.addLineToPoint(CGPointMake(frame.minX + 0.75714 * frame.width, frame.minY + 0.64286 * frame.height))
bezierPath.addLineToPoint(CGPointMake(frame.minX + 0.61429 * frame.width, frame.minY + 0.50536 * frame.height))
bezierPath.closePath()
bezierPath.lineJoinStyle = kCGLineJoinRound;
color2.setFill()
bezierPath.fill()
UIColor.whiteColor().setStroke()
bezierPath.lineWidth = 3
bezierPath.stroke()
// }
}
}
Change the references to 'frame' to 'rect' and it should draw in the device and simulator.
You should not be overriding drawRect: for a UIButton subclass. There is no need; you can customize the look of a UIButton using its various properties and methods. It is far from clear why you are doing this. (The fact that you are doing it, and not calling super, probably explains the problem; but the solution is not, call super, but rather, don't do it in the first place.)
I have been trying to do a similar thing recently - create a subclass of UIButton, overriding drawRect() in order to draw a simple custom icon (I am making a play/stop button that alternates between "Play" and "Stop" on each tap):
Maybe I don't know enough about iOS development yet, but in response to other answers and older posts online that discourage extending the UIButton class, I don't understand why this would be a bad idea. Of course you could customize a button by setting custom background images, but in my opinion this isn't as flexible as overriding drawRect (not to mention it can be a pain to edit your button anytime you want to change something because you have to edit an actual image instead of a few lines of code).
I'm sure there are other ways to achieve the same result (implement a button via a custom UIView listening for touch events, for example), but I think the cleanest and easiest way is still just to extend the UIButton class. After all, it is a subclass itself of UIView.
Regarding your code - it appears to work correctly in my environment, although I had to change the colors slightly to get it to show up against a white background (i.e. UIColor.blackColor().setStroke() instead of UIColor.whiteColor().setStroke()). Also, as #Astrodan mentioned, it might be a good idea to use rect instead of frame, since it is being passed to you (although the code worked with frame for me):
Live-rendered Storyboard in XCode:
On iPhone 6 Simulator:
I was wondering how I could create a more complex SKPhysicsyBody like this? I want to hittest againt the black parts.
I exported this shape with PaintCode to a CG Rect.
var barrierpath2Path = UIBezierPath()
barrierpath2Path.moveToPoint(CGPointMake(52, 26))
barrierpath2Path.addLineToPoint(CGPointMake(52, 26))
barrierpath2Path.addCurveToPoint(CGPointMake(26, 52), controlPoint1: CGPointMake(52, 40.36), controlPoint2: CGPointMake(40.36, 52))
barrierpath2Path.addLineToPoint(CGPointMake(26, 52))
barrierpath2Path.addCurveToPoint(CGPointMake(0, 26), controlPoint1: CGPointMake(11.64, 52), controlPoint2: CGPointMake(0, 40.36))
barrierpath2Path.addLineToPoint(CGPointMake(0, 26))
barrierpath2Path.addCurveToPoint(CGPointMake(26, 0), controlPoint1: CGPointMake(0, 11.64), controlPoint2: CGPointMake(11.64, 0))
barrierpath2Path.addLineToPoint(CGPointMake(26, 0))
barrierpath2Path.addCurveToPoint(CGPointMake(52, 26), controlPoint1: CGPointMake(40.36, 0), controlPoint2: CGPointMake(52, 11.64))
barrierpath2Path.closePath()
barrierpath2Path.moveToPoint(CGPointMake(46.8, 26))
barrierpath2Path.addLineToPoint(CGPointMake(46.8, 26))
barrierpath2Path.addCurveToPoint(CGPointMake(39, 18.2), controlPoint1: CGPointMake(46.8, 21.69), controlPoint2: CGPointMake(43.31, 18.2))
barrierpath2Path.addLineToPoint(CGPointMake(39, 18.2))
barrierpath2Path.addCurveToPoint(CGPointMake(31.2, 26), controlPoint1: CGPointMake(34.69, 18.2), controlPoint2: CGPointMake(31.2, 21.69))
barrierpath2Path.addLineToPoint(CGPointMake(31.2, 26))
barrierpath2Path.addCurveToPoint(CGPointMake(39, 33.8), controlPoint1: CGPointMake(31.2, 30.31), controlPoint2: CGPointMake(34.69, 33.8))
barrierpath2Path.addLineToPoint(CGPointMake(39, 33.8))
barrierpath2Path.addCurveToPoint(CGPointMake(46.8, 26), controlPoint1: CGPointMake(43.31, 33.8), controlPoint2: CGPointMake(46.8, 30.31))
barrierpath2Path.closePath()
barrierpath2Path.moveToPoint(CGPointMake(27.3, 14.74))
barrierpath2Path.addLineToPoint(CGPointMake(27.3, 14.74))
barrierpath2Path.addCurveToPoint(CGPointMake(19.5, 6.94), controlPoint1: CGPointMake(27.3, 10.43), controlPoint2: CGPointMake(23.81, 6.94))
barrierpath2Path.addLineToPoint(CGPointMake(19.5, 6.94))
barrierpath2Path.addCurveToPoint(CGPointMake(11.7, 14.74), controlPoint1: CGPointMake(15.19, 6.94), controlPoint2: CGPointMake(11.7, 10.43))
barrierpath2Path.addLineToPoint(CGPointMake(11.7, 14.74))
barrierpath2Path.addCurveToPoint(CGPointMake(19.5, 22.54), controlPoint1: CGPointMake(11.7, 19.05), controlPoint2: CGPointMake(15.19, 22.54))
barrierpath2Path.addLineToPoint(CGPointMake(19.5, 22.54))
barrierpath2Path.addCurveToPoint(CGPointMake(27.3, 14.74), controlPoint1: CGPointMake(23.81, 22.54), controlPoint2: CGPointMake(27.3, 19.05))
barrierpath2Path.closePath()
barrierpath2Path.moveToPoint(CGPointMake(27.3, 37.26))
barrierpath2Path.addLineToPoint(CGPointMake(27.3, 37.26))
barrierpath2Path.addCurveToPoint(CGPointMake(19.5, 29.46), controlPoint1: CGPointMake(27.3, 32.95), controlPoint2: CGPointMake(23.81, 29.46))
barrierpath2Path.addLineToPoint(CGPointMake(19.5, 29.46))
barrierpath2Path.addCurveToPoint(CGPointMake(11.7, 37.26), controlPoint1: CGPointMake(15.19, 29.46), controlPoint2: CGPointMake(11.7, 32.95))
barrierpath2Path.addLineToPoint(CGPointMake(11.7, 37.26))
barrierpath2Path.addCurveToPoint(CGPointMake(19.5, 45.06), controlPoint1: CGPointMake(11.7, 41.57), controlPoint2: CGPointMake(15.19, 45.06))
barrierpath2Path.addLineToPoint(CGPointMake(19.5, 45.06))
barrierpath2Path.addCurveToPoint(CGPointMake(27.3, 37.26), controlPoint1: CGPointMake(23.81, 45.06), controlPoint2: CGPointMake(27.3, 41.57))
barrierpath2Path.closePath()
barrierpath2Path.miterLimit = 4;
monster.physicsBody = SKPhysicsBody(polygonFromPath: barrierpath2Path.CGPath)
But the simulator shows just one hole (and the png behind)? What's wrong here?
You most likely want to make the holes child nodes with their own physics bodies. This will allow everything to have their own respective physics bodies and move in accordance to each other without any problems
To further elaborate on this answer, you might also be able to use the SKPhysicsBody convenience initializer to create a physics body off of the alpha values in a texture. This will incur a performance hit, but should do what you want it to.
l.physicsBody = SKPhysicsBody(texture: l.texture, size: l.size)