Improve SceneKit Playground Speed - ios

I am testing some simple SceneKit runtime node creation of planes (only about 30 planes) and would prefer to use a Playground to test concepts. Normally Playgrounds run reasonably fast but with SceneKit the drawing that normally takes a fraction of a second is taking minutes. Here is my code which draws a simple 5x5 maze
import UIKit
import SceneKit
import PlaygroundSupport // needed to create the live view
let maze = Maze(mazeSizeX, mazeSizeY)
public let π = M_PI
maze.solveMaze(x: mazeSizeX-1, y: mazeSizeY-1, comingFrom: -1)
maze.displayWithSolution()
let cellSize: CGFloat = 1.0
let scene = SCNScene()
let blueMat = SCNMaterial()
blueMat.diffuse.contents = #colorLiteral(red: 0.9529411793, green: 0.6862745285, blue: 0.1333333403, alpha: 1)
blueMat.lightingModel = SCNMaterial.LightingModel.physicallyBased
let redMat = SCNMaterial()
redMat.diffuse.contents = #colorLiteral(red: 0.9254902005, green: 0.2352941185, blue: 0.1019607857, alpha: 1)
redMat.lightingModel = SCNMaterial.LightingModel.physicallyBased
let greenMat = SCNMaterial()
greenMat.diffuse.contents = #colorLiteral(red: 0.3411764801, green: 0.6235294342, blue: 0.1686274558, alpha: 1)
greenMat.lightingModel = SCNMaterial.LightingModel.physicallyBased
let purpleMat = SCNMaterial()
purpleMat.diffuse.contents = #colorLiteral(red: 0.5568627715, green: 0.3529411852, blue: 0.9686274529, alpha: 1)
purpleMat.lightingModel = SCNMaterial.LightingModel.physicallyBased
let planeN = SCNPlane(width: cellSize, height: cellSize)
let planeS = SCNPlane(width: cellSize, height: cellSize)
let planeE = SCNPlane(width: cellSize, height: cellSize)
let planeW = SCNPlane(width: cellSize, height: cellSize)
planeN.materials = [blueMat]
planeS.materials = [redMat]
planeE.materials = [greenMat]
planeW.materials = [purpleMat]
//plane.firstMaterial?.isDoubleSided = true
for x in 0 ..< mazeSizeX
{
let xPos: CGFloat = CGFloat(x)
for y in 0 ..< mazeSizeY
{
let yPos: CGFloat = CGFloat(y)
if maze.isWallThere(x: Double(x), y: Double(y), side: North)
{
let planeNode = SCNNode(geometry: planeN)
planeNode.rotation = SCNVector4(-1, 0, 0, π/2)
planeNode.position = SCNVector3(-yPos*cellSize, (xPos-0.5)*cellSize, cellSize/2.0)
scene.rootNode.addChildNode(planeNode)
}
if maze.isWallThere(x: Double(x), y: Double(y), side: South)
{
let planeNode = SCNNode(geometry: planeS)
planeNode.rotation = SCNVector4(1, 0, 0, π/2)
planeNode.position = SCNVector3(-yPos*cellSize, (xPos+0.5)*cellSize, cellSize/2.0)
scene.rootNode.addChildNode(planeNode)
}
if maze.isWallThere(x: Double(x), y: Double(y), side: East)
{
let planeNode = SCNNode(geometry: planeE)
planeNode.rotation = SCNVector4(0, 1, 0, π/2)
planeNode.position = SCNVector3((-yPos-0.5)*cellSize, xPos*cellSize, cellSize/2.0)
scene.rootNode.addChildNode(planeNode)
}
if maze.isWallThere(x: Double(x), y: Double(y), side: West)
{
let planeNode = SCNNode(geometry: planeW)
planeNode.rotation = SCNVector4(0, -1, 0, π/2)
planeNode.position = SCNVector3((-yPos+0.5)*cellSize, xPos*cellSize, cellSize/2.0)
scene.rootNode.addChildNode(planeNode)
}
}
}
let floor = SCNNode(geometry: SCNBox(width: CGFloat(mazeSizeX), height: CGFloat(mazeSizeY), length: 0.1, chamferRadius: 0))
floor.position = SCNVector3(-(CGFloat(mazeSizeX)/2.0)+0.5, (CGFloat(mazeSizeY)/2.0)-0.5, 0)
scene.rootNode.addChildNode(floor)
let light = SCNLight()
light.type = SCNLight.LightType.omni
let lightNode = SCNNode()
lightNode.light = light
lightNode.position = SCNVector3(40,12,15)
scene.rootNode.addChildNode(lightNode)
let light2 = SCNLight()
light2.type = SCNLight.LightType.omni
let lightNode2 = SCNNode()
lightNode2.light = light
lightNode2.position = SCNVector3(-40,-24,-30)
scene.rootNode.addChildNode(lightNode2)
var cameraPosition = SCNVector3Make(0.5, 0.5, 0.5)
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.position = cameraPosition
cameraNode.rotation = SCNVector4(-1, 0, 0, π/2)
scene.rootNode.addChildNode(cameraNode)
//let view = SCNView() //iPad version
let view = SCNView(frame: CGRect(x: 0, y: 0, width: 400, height: 600)) //Xcode version
view.allowsCameraControl = true
view.autoenablesDefaultLighting = true
view.showsStatistics = true
view.scene = scene
view.backgroundColor = #colorLiteral(red: 0.8078431487, green: 0.02745098062, blue: 0.3333333433, alpha: 1)
PlaygroundPage.current.liveView = view
Is there anything I am doing wrong or could do to optimize the SceneKit drawing in a Playground?

I do my SceneKit Playground work in macOS whenever possible. The performance just isn't there on iOS Playgrounds.

Related

SceneKit. How to create box with rounded edges and with different radiuses?

I've started learning SceneKit. And I've tried SCNBox. And it has chamferRadius. But the radius is applied for all the edges.
But I want to achieve something similar to the one on the screenshot below
You can do this by extruding a UIBezierPath:
// rounded rect bezier path
let path = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: 1.0, height: 1.0), cornerRadius: 0.1)
path.flatness = 0
// extrude the path
let shape = SCNShape(path: path, extrusionDepth: 0.05)
let mat = SCNMaterial()
mat.diffuse.contents = UIColor(white: 0.9, alpha: 1.0)
shape.materials = [mat]
let shapeNode = SCNNode(geometry: shape)
Result:
Here's a full example (note: I have only glanced at SceneKit, so I used this tutorial as a starting point Introduction to SceneKit):
import UIKit
import SceneKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let sceneView = SCNView(frame: self.view.frame)
self.view.addSubview(sceneView)
let scene = SCNScene()
sceneView.scene = scene
let camera = SCNCamera()
let cameraNode = SCNNode()
cameraNode.camera = camera
cameraNode.position = SCNVector3(x: 3.0, y: 2.0, z: 1.5)
let ambientLight = SCNLight()
ambientLight.type = .ambient
ambientLight.color = UIColor(white: 0.9, alpha: 1.0)
cameraNode.light = ambientLight
let light = SCNLight()
light.type = SCNLight.LightType.spot
light.spotInnerAngle = 30.0
light.spotOuterAngle = 80.0
light.castsShadow = true
let lightNode = SCNNode()
lightNode.light = light
lightNode.position = SCNVector3(x: 1.5, y: 1.5, z: 1.5)
// rounded rect bezier path
let path = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: 1.0, height: 1.0), cornerRadius: 0.1)
path.flatness = 0
// extrude the path
let shape = SCNShape(path: path, extrusionDepth: 0.05)
let mat = SCNMaterial()
mat.diffuse.contents = UIColor(white: 0.9, alpha: 1.0)
shape.materials = [mat]
let shapeNode = SCNNode(geometry: shape)
let planeGeometry = SCNPlane(width: 50.0, height: 50.0)
let planeNode = SCNNode(geometry: planeGeometry)
planeNode.eulerAngles = SCNVector3(x: GLKMathDegreesToRadians(-90), y: 0, z: 0)
planeNode.position = SCNVector3(x: 0, y: 0.0, z: 0)
let floorMaterial = SCNMaterial()
floorMaterial.diffuse.contents = UIColor.lightGray
planeGeometry.materials = [floorMaterial]
scene.rootNode.addChildNode(lightNode)
scene.rootNode.addChildNode(cameraNode)
scene.rootNode.addChildNode(shapeNode)
scene.rootNode.addChildNode(planeNode)
let constraint = SCNLookAtConstraint(target: shapeNode)
constraint.isGimbalLockEnabled = true
cameraNode.constraints = [constraint]
lightNode.constraints = [constraint]
}
}

How can I make animation with CAEmitterLayer on SwiftUI?

How can I convert this code to SwiftUI. It's a snow effect. I used CAEmitterLayer but I don't know how to use it in SwfitUI. There is no addSublayer in SwiftUI. Is it possible to run this code without using UIHostingController ?
let size = CGSize(width: 824.0, height: 1112.0)
let host = UIView(frame: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height))
self.view.addSubview(host)
let particlesLayer = CAEmitterLayer()
particlesLayer.frame = CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)
host.layer.addSublayer(particlesLayer)
host.layer.masksToBounds = true
particlesLayer.backgroundColor = UIColor(red: 0.0/255.0, green: 0.0/255.0, blue: 0.0/255.0, alpha: 1.0).cgColor
particlesLayer.emitterShape = .circle
particlesLayer.emitterPosition = CGPoint(x: 509.4, y: 707.7)
particlesLayer.emitterSize = CGSize(width: 1648.0, height: 1112.0)
particlesLayer.emitterMode = .surface
particlesLayer.renderMode = .oldestLast
let image1 = UIImage(named: "logo")?.cgImage
let cell1 = CAEmitterCell()
cell1.contents = image1
cell1.name = "Snow"
cell1.birthRate = 92.0
cell1.lifetime = 20.0
cell1.velocity = 59.0
cell1.velocityRange = -15.0
cell1.xAcceleration = 5.0
cell1.yAcceleration = 40.0
cell1.emissionRange = 180.0 * (.pi / 180.0)
cell1.spin = -28.6 * (.pi / 180.0)
cell1.spinRange = 57.2 * (.pi / 180.0)
cell1.scale = 0.06
cell1.scaleRange = 0.3
cell1.color = UIColor(red: 255.0/255.0, green: 255.0/255.0, blue: 255.0/255.0, alpha: 1.0).cgColor
particlesLayer.emitterCells = [cell1]
Here is a solution. Tested with Xcode 11.4 / iOS 13.4
struct EmitterView: UIViewRepresentable {
func makeUIView(context: Context) -> UIView {
let size = CGSize(width: 824.0, height: 1112.0)
let host = UIView(frame: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height))
let particlesLayer = CAEmitterLayer()
particlesLayer.frame = CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)
host.layer.addSublayer(particlesLayer)
host.layer.masksToBounds = true
particlesLayer.backgroundColor = UIColor(red: 0.0/255.0, green: 0.0/255.0, blue: 0.0/255.0, alpha: 1.0).cgColor
particlesLayer.emitterShape = .circle
particlesLayer.emitterPosition = CGPoint(x: 509.4, y: 707.7)
particlesLayer.emitterSize = CGSize(width: 1648.0, height: 1112.0)
particlesLayer.emitterMode = .surface
particlesLayer.renderMode = .oldestLast
let image1 = UIImage(named: "logo")?.cgImage
let cell1 = CAEmitterCell()
cell1.contents = image1
cell1.name = "Snow"
cell1.birthRate = 92.0
cell1.lifetime = 20.0
cell1.velocity = 59.0
cell1.velocityRange = -15.0
cell1.xAcceleration = 5.0
cell1.yAcceleration = 40.0
cell1.emissionRange = 180.0 * (.pi / 180.0)
cell1.spin = -28.6 * (.pi / 180.0)
cell1.spinRange = 57.2 * (.pi / 180.0)
cell1.scale = 0.06
cell1.scaleRange = 0.3
cell1.color = UIColor(red: 255.0/255.0, green: 255.0/255.0, blue: 255.0/255.0, alpha: 1.0).cgColor
particlesLayer.emitterCells = [cell1]
return host
}
func updateUIView(_ uiView: UIView, context: Context) {
}
typealias UIViewType = UIView
}
struct TestEmitterLayer: View {
var body: some View {
EmitterView() // << usage !!
}
}

How to find what sublayer was pressed?

I have a UIImageView, with 2 sublayers, representing 2 different images.
Is there a way to determine which layer was pressed?
Current code
let point = sender.location(in: flagImageView) // Where you pressed
guard let sublayers = flagImageView.layer.sublayers else { return }
if let layer = sublayers.first(where: {$0.name == "Upper_Image"})?.hitTest(point) {
print("Found it: \(layer)")
}
This is the method to add the images
let distance: CGFloat = 4
let downMaskPath = UIBezierPath()
downMaskPath.move(to: CGPoint(x: 0, y: 0))
downMaskPath.addLine(to: CGPoint(x: self.frame.width - distance, y: 0))
downMaskPath.addLine(to: CGPoint(x: 0, y: self.frame.height))
downMaskPath.close()
let upperMaskPath = UIBezierPath()
upperMaskPath.move(to: CGPoint(x: self.frame.width, y: 0))
upperMaskPath.addLine(to: CGPoint(x: self.frame.width, y: self.frame.height))
upperMaskPath.addLine(to: CGPoint(x: distance, y: self.frame.height))
upperMaskPath.close()
upperMaskPath.stroke()
let imageLayer1 = CALayer()
imageLayer1.borderColor = #colorLiteral(red: 0.07843137255, green: 0.1294117647, blue: 0.7176470588, alpha: 1).cgColor
imageLayer1.borderWidth = 1
imageLayer1.contents = image1?.cgImage // Assign your image
imageLayer1.frame = self.frame // Define a frame
let imageLayer2 = CALayer()
imageLayer2.borderColor = #colorLiteral(red: 0, green: 0.4431372549, blue: 1, alpha: 1).cgColor
imageLayer2.borderWidth = 1
imageLayer2.contents = image2?.cgImage // Assign your image
imageLayer2.frame = self.frame // Define a frame
let maskLayer1 = CAShapeLayer()
maskLayer1.path = downMaskPath.cgPath
imageLayer1.mask = maskLayer1
let maskLayer2 = CAShapeLayer()
maskLayer2.path = upperMaskPath.cgPath
imageLayer2.mask = maskLayer2
self.layer.addSublayer(imageLayer1)
self.layer.addSublayer(imageLayer2)
You can use the name property for CALayer, to uniquely identify the among various layers.
Like so :
imageLayer1.name = "someName"
As per Apple
/* The name of the layer. Used by some layout managers. Defaults to nil. */
/** Miscellaneous properties. **/
open var name: String?

UIView bounds.applying but with rotation

I'd like to create a dash border around a view, which can be moved/rotated/scaled.
Here's my code:
func addBorder() {
let f = selectedObject.bounds.applying(selectedObject.transform)
borderView.backgroundColor = UIColor(red: 1, green: 0, blue: 0, alpha: 0.5) //just for testing
borderView.frame = f
borderView.center = selectedObject.center
borderView.transform = CGAffineTransform(translationX: selectedObject.transform.tx, y: selectedObject.transform.ty)
removeBorder() //remove old border
let f2 = CGRect(x: 0, y: 0, width: borderView.frame.width, height: borderView.frame.height)
let dashedBorder = CAShapeLayer()
dashedBorder.strokeColor = UIColor.black.cgColor
dashedBorder.lineDashPattern = [2, 2]
dashedBorder.frame = f2
dashedBorder.fillColor = nil
dashedBorder.path = UIBezierPath(rect: f2).cgPath
dashedBorder.name = "border"
borderView.layer.addSublayer(dashedBorder)
}
And it looks like this:
It's not bad, but I want the border to be rotated as well, because it may be misleading for the user as touch area is only on the image.
I've tried to apply rotation to the transform:
func addBorder() {
let f = selectedObject.bounds.applying(selectedObject.transform)
borderView.backgroundColor = UIColor(red: 1, green: 0, blue: 0, alpha: 0.5) //just for testing
borderView.frame = f
borderView.center = selectedObject.center
let rotation = atan2(selectedObject.transform.b, selectedObject.transform.a)
borderView.transform = CGAffineTransform(rotationAngle: rotation).translatedBy(x: selectedObject.transform.tx, y: selectedObject.transform.ty)
removeBorder() //remove old border
let f2 = CGRect(x: 0, y: 0, width: borderView.frame.width, height: borderView.frame.height)
let dashedBorder = CAShapeLayer()
dashedBorder.strokeColor = UIColor.black.cgColor
dashedBorder.lineDashPattern = [2, 2]
dashedBorder.frame = f2
dashedBorder.fillColor = nil
dashedBorder.path = UIBezierPath(rect: f2).cgPath
dashedBorder.name = "border"
borderView.layer.addSublayer(dashedBorder)
}
But after rotating it looks like this:
How can I fix this?
Here is a sample based on your code that should do:
//initial transforms
selectedObject.transform = CGAffineTransform.init(rotationAngle: .pi / 4).translatedBy(x: 150, y: 15)
func addBorder() {
let borderView = UIView.init(frame: selectedObject.bounds)
self.view.addSubview(borderView)
borderView.backgroundColor = UIColor(red: 1, green: 0, blue: 0, alpha: 0.5) //just for testing
borderView.center = selectedObject.center
borderView.transform = selectedObject.transform
removeBorder() //remove old border
let dashedBorder = CAShapeLayer()
dashedBorder.strokeColor = UIColor.black.cgColor
dashedBorder.lineDashPattern = [2, 2]
dashedBorder.fillColor = nil
dashedBorder.path = UIBezierPath(rect: borderView.bounds).cgPath
dashedBorder.name = "border"
borderView.layer.addSublayer(dashedBorder)
}
Here is the solution of for problem:
func addBorder() {
borderView.backgroundColor = UIColor(red: 1, green: 0, blue: 0, alpha: 0.5) //just for testing
let degrees: CGFloat = 20.0 //the value in degrees for rotation
let radians: CGFloat = degrees * (.pi / 180)
borderView.transform = CGAffineTransform(rotationAngle: radians)
removeBorder()
let dashedBorder = CAShapeLayer()
dashedBorder.strokeColor = UIColor.black.cgColor
dashedBorder.lineDashPattern = [2, 2]
dashedBorder.frame = borderView.bounds
dashedBorder.fillColor = nil
dashedBorder.path = UIBezierPath(roundedRect: borderView.bounds, cornerRadius:0).cgPath
dashedBorder.name = "border"
borderView.layer.addSublayer(dashedBorder)
}
The above code is tested in Xcode 10 with Swift 4.2
Even though I've accepted the answer, because it helped me understand the issue I'm posting the final answer, because it's more to it. And I think it can be helpful for someone else, because I couldn't find this solution on Stackoverflow or somewhere else.
The idea is to create a borderView with bounds same as selectedObject. This was the solution from #Incredible_dev, however there was one issue: the line itself stretches as the borderView is scaled in any direction. And I want to keep the line size and just it want to be around selectedObject. So, I multiply selectedObject bounds with scale extracted from selectedObject.transform. Then I copy translation and rotation from the selectedObject.
Here's the final code:
var borderView: UIView!
var selectedObject: UIView?
extension CGAffineTransform { //helper extension
func getScale() -> CGFloat {
return (self.a * self.a + self.c * self.c).squareRoot()
}
func getRotation() -> CGFloat {
return atan2(self.b, self.a)
}
}
func removeBorder() { //remove the older border
if borderView != nil {
borderView.removeFromSuperview()
}
}
func addBorder() {
guard let selectedObject = selectedObject else { return }
removeBorder() //remove old border
let t = selectedObject.transform
let s = t.getScale()
let r = t.getRotation()
borderView = UIView(frame: CGRect(x: 0, y: 0, width: selectedObject.bounds.width * s, height: selectedObject.bounds.height * s)) //multiply bounds with selectedObject's scale
dividerImageView.addSubview(borderView) //add borderView to the "scene"
borderView.transform = CGAffineTransform(translationX: t.tx, y: t.ty).rotated(by: r) //copy translation and rotation, order is important
borderView.center = selectedObject.center
let dashedBorder = CAShapeLayer() //create 2-point wide dashed line
dashedBorder.lineWidth = 2
dashedBorder.strokeColor = UIColor.black.cgColor
dashedBorder.lineDashPattern = [2, 2]
dashedBorder.fillColor = nil
dashedBorder.path = UIBezierPath(rect: borderView.bounds).cgPath
borderView.layer.addSublayer(dashedBorder)
}

After CAReplicatorLayer animation in a `vc`'s `subview`, switch `vc` comes a strange issue

CAReplicator did not keep the state after the switch vc:
Dots of CAReplicator did not keep its scale after the vc switch back.
As you see, the circle animation is created by CAReplicator.
after the main vc switch to another vc, then switch back, the Circle's dots become very small. witch is set in the initial.
My code is below:
In the main vc:
func initUI() {
let lml_frame = CGRect.init(x: 0, y: 64, width: self.view.bounds.size.width, height: 400)
lml_digtal_view = LMLDigitalDazzleAnimationView.init(frame: lml_frame)
self.view.addSubview(lml_digtal_view!)
}
In the LMLDigitalDazzleAnimationView:
import Foundation
import UIKit
class LMLDigitalDazzleAnimationView: UIView {
/*
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
// Drawing code
}
*/
var initFrame = CGRect.init(x: 0, y: 0, width: 320, height: 480)
var fromColor = UIColor.init(red: 240/255.0, green: 77.0/255.0, blue: 48.0/255.0, alpha: 1.0).cgColor
var toColor = UIColor.init(red: 220.0/255.0, green: 28.0/255.0, blue: 44.0/255.0, alpha: 1.0).cgColor
var money:Float? = 1200.25 {
didSet {
}
}
override init(frame: CGRect) {
super.init(frame: frame)
initFrame = frame
initUI()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func initUI(){
let gradul_layer = CAGradientLayer.init()
gradul_layer.frame = CGRect.init(x: 0, y: 0, width: initFrame.width, height: initFrame.height)
gradul_layer.colors = [
fromColor,
toColor
]
gradul_layer.startPoint = CGPoint.init(x: 0.5, y: 0.3)
gradul_layer.endPoint = CGPoint.init(x: 0.5, y: 0.7)
layer.addSublayer(gradul_layer)
let wave_view0 = KHWaveView.init(frame: CGRect.init(x: 0, y: initFrame.height - 80, width: initFrame.width, height: 80))
//wave_view.backgroundColor = UIColor.white
wave_view0.waveColor = UIColor.init(red: 1, green: 1, blue: 1, alpha: 0.5)
wave_view0.waveSpeed = 1.3
wave_view0.waveTime = 0
wave_view0.wave()
self.addSubview(wave_view0)
let wave_view = KHWaveView.init(frame: CGRect.init(x: 0, y: initFrame.height - 80, width: initFrame.width, height: 80))
//wave_view.backgroundColor = UIColor.white
wave_view.waveColor = UIColor.white
wave_view.waveSpeed = 1.0
wave_view.waveTime = 0
wave_view.wave()
self.addSubview(wave_view)
animateCircle()
animateDigitalIcrease(money: money!)
}
func animateCircle() -> Void {
let r = CAReplicatorLayer()
r.bounds = CGRect(x:0.0, y:0.0, width:260.0, height:260.0)
r.cornerRadius = 10.0
r.backgroundColor = UIColor.clear.cgColor
r.position = CGPoint.init(x: self.bounds.width / 2.0, y: 160)
self.layer.addSublayer(r)
let dot = CALayer()
dot.bounds = CGRect(x:0.0, y :0.0, width:6.0, height:6.0)
dot.position = CGPoint(x:100.0, y:10.0)
dot.backgroundColor = UIColor(white:1, alpha:1.0).cgColor
dot.cornerRadius = 3.0
r.addSublayer(dot)
let nrDots: Int = 32
r.instanceCount = nrDots
let angle = CGFloat(2*M_PI) / CGFloat(nrDots)
r.instanceTransform = CATransform3DMakeRotation(angle, 0.1, 0.1, 1.0)
let duration:CFTimeInterval = 1.5
let shrink = CABasicAnimation(keyPath: "transform.scale")
shrink.fromValue = 1.0
shrink.toValue = 1.0 // 0.5
shrink.duration = duration
shrink.repeatCount = Float.infinity
dot.add(shrink, forKey: nil)
r.instanceDelay = duration/Double(nrDots)
dot.transform = CATransform3DMakeScale(0.1, 0.1, 0.1)
delay(delay: duration) {
let turn_key_path = "transform.rotation"
let turn_ani = CABasicAnimation.init(keyPath: turn_key_path)
turn_ani.isRemovedOnCompletion = false
turn_ani.fillMode = kCAFillModeForwards
turn_ani.toValue = M_PI*2
turn_ani.duration = 2.0
turn_ani.repeatCount = 2
r.add(turn_ani, forKey: turn_key_path)
}
}
func delay(delay:Double, closure:#escaping ()->()){
let when = DispatchTime.now() + delay
DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
}
func animateDigitalIcrease(money :Float){
let frame = CGRect.init(x: 0, y: 0, width: 120, height: 80)
let counterLabel = LMLDigitalIncreaseLabel.init(frame: frame, andDuration: 2.0, andFromValue: 0, andToValue: money)
counterLabel?.center = CGPoint.init(x: self.bounds.size.width / 2.0, y: 130)
self.addSubview(counterLabel!)
counterLabel?.start()
delay(delay: 5.0) {
counterLabel?.stop()
self.animateFadeShowSmallMoney()
}
}
func animateFadeShowSmallMoney(){
let border_view = UIView.init(frame: CGRect.init(x: 0, y: 0, width: 100, height: 30))
border_view.layer.cornerRadius = 15
border_view.layer.masksToBounds = true
border_view.layer.borderWidth = 1
border_view.backgroundColor = UIColor.clear
border_view.layer.borderColor = UIColor.white.cgColor
let small_money_frame = CGRect.init(x: 0, y: 0, width: 80, height: 30)
let small_money = UILabel.init(frame: small_money_frame)
small_money.center = border_view.center
small_money.adjustsFontSizeToFitWidth = true
small_money.textAlignment = NSTextAlignment.center
small_money.text = "mo:" + String(format:"%.2f", money!)
small_money.textColor = UIColor.white
border_view.addSubview(small_money)
border_view.alpha = 0.0
self.addSubview(border_view)
border_view.center = CGPoint.init(x: self.bounds.size.width/2.0, y: 220)
UIView.animate(withDuration: 1.0) {
border_view.alpha = 1.0
}
}
}
My code is not good, you can advice me how to encapsulate a animation class better.
After many attention, I solve my issue:
delay(delay: duration) {
let turn_key_path = "transform.rotation"
let turn_ani = CABasicAnimation.init(keyPath: turn_key_path)
turn_ani.isRemovedOnCompletion = false
turn_ani.fillMode = kCAFillModeForwards
turn_ani.toValue = M_PI*2
turn_ani.duration = 2.0
turn_ani.repeatCount = 2
r.add(turn_ani, forKey: turn_key_path)
dot.transform = CATransform3DMakeScale(1, 1, 1) // add this line solve my issue.
}

Resources