3D rotating object animation on iOS - ios

I want to replicate the 3D rotating object shown at the 6th second of this video on iOS. I don't have any 3D animations/games experience, so I don't know where to start. I have looked a little and found this tutorial of Ray Wenderlich. I think I can change the shape from cube to a rectangular prism, and move it around while it's rotating, but I want it to have some logo, text, etc. on it in 3D. This is where I'm stuck.
Any suggestions or tutorials related to this kind of work?

You need to get familiar with general concepts of 3D programming first because your question is ambiguous. What do you mean by on it?
That cube is comprised of several different arrays of data defining different properties of each vertex on each triangular face. So if you want 3D text coming out of the cube you will need to redefine those arrays.
There are a tonne of resources for learning 3D programing and the concepts are generally universal.

This is what i have done for rotate cube. Hope it helps.
var cubeLayer = CATransformLayer()
var layer1 = CALayer()
var layer2 = CALayer()
var layer3 = CALayer()
var layer4 = CALayer()
var pt = CATransform3DIdentity
pt.m34 = -1.0 / 500.0
yourView.layer.sublayerTransform = pt
layer1.backgroundColor = UIColor.red.cgColor
layer1.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
layer1.transform = CATransform3DTranslate(layer1.transform, 0, 0, 100)
cubeLayer.addSublayer(layer1)
layer2.backgroundColor = UIColor.green.cgColor
layer2.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
layer2.transform = CATransform3DTranslate(layer2.transform, -100, 0, 0)
layer2.transform = CATransform3DRotate(layer2.transform, .pi / 2, 0, 1, 0)
cubeLayer.addSublayer(layer2)
layer3.backgroundColor = UIColor.yellow.cgColor
layer3.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
layer3.transform = CATransform3DTranslate(layer3.transform, 100, 0, 0)
layer3.transform = CATransform3DRotate(layer3.transform, .pi / 2, 0, 1, 0)
cubeLayer.addSublayer(layer3)
layer4.backgroundColor = UIColor.blue.cgColor
layer4.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
layer4.transform = CATransform3DTranslate(layer4.transform, 0, 0, -100)
cubeLayer.addSublayer(layer4)
cubeLayer.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
yourView.layer.addSublayer(cubeLayer)
add gesture to your view and rotate it using swap gesture, using below code you can rotate cube from left to right.
let swiperight = UISwipeGestureRecognizer(target: self, action: #selector(self.swiperight))
swiperight.direction = .right
yourView.addGestureRecognizer(swiperight)
#objc func swiperight(_ gestureRecognizer: UISwipeGestureRecognizer) {
self.cubeLayer.transform = CATransform3DRotate(self.cubeLayer.transform, .pi / -1.5, 0, 1, 0)
self.cubeLayer.transform = CATransform3DRotate(self.cubeLayer.transform, .pi / 4
, 0, 1, 0)
self.cubeLayer.transform = CATransform3DRotate(self.cubeLayer.transform, .pi / 2
, 0, 1, 0)
}

Related

How to Bend or distortion a UILabel Text from one side

I have used attributed String and Libraries but unable to find any proper solution for Label Text effect.
Can Anyone please suggest me how i can achieve this functionality.?
Thanks
Adapting the code from How do I apply a perspective transform to a UIView?, here is some sample Swift 4 code that applies perspective to a label giving a result similar to your image.
let pview = UIView(frame: CGRect(x: 0, y: 0, width: 400, height: 300))
pview.backgroundColor = .black
let plabel = UILabel(frame: CGRect(x: 0, y: 110, width: 250, height: 75))
pview.addSubview(plabel)
plabel.text = " ND420 "
plabel.textAlignment = .center
plabel.font = UIFont.boldSystemFont(ofSize: 72)
plabel.textColor = .orange
plabel.backgroundColor = .red
plabel.sizeToFit()
let layer = plabel.layer
var transform = CATransform3DIdentity
transform.m34 = 1.0 / -200
transform = CATransform3DRotate(transform, -45 * CGFloat.pi / 180, 0, 1, 0)
layer.transform = transform
Run this in the playground and view pview.
Play with the value being assigned to transform.m34 and the rotation angle in CATransform3DRotate. A negative rotation angle (as shown above) makes the left side smaller and the right side larger. A positive angle does the opposite.

UIView as SCNMaterialProperty on iOS

When using a UIView as an SCNMaterialProperty in a SceneKit scene, can that view be part of the responder chain?
To clarify how:
omg_you_can_use_an_actual_uiview_now = UIView(frame: CGRect(x: 0, y: 0, width: 500, height: 500))
material_plane.diffuse.contents = omg_you_can_use_an_actual_uiview_now
let geom_plane = SCNPlane(width: 1, height: 1)
geom_plane.materials = [material_plane]

Transform UIView within bounds

I am trying to make a view transform and kind of have a circle effect until it reaches certain points then it just fills a rectangle. This is for a material design project I am working on. All the code is in Swift 2.0 on an iOS 8 or above device.
func helloWorld(sender: UIButton) {
let point = sender.frame.origin
let rippleViewInitFrame: CGRect = CGRect(x: point.x, y: point.y, width: 4, height: 4)
let rippleView: UIView = UIView(frame: rippleViewInitFrame)
rippleView.backgroundColor = UIColor.MDColor.blue
let bounds: CGRect = CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height)
rippleView.layer.masksToBounds = true
rippleView.layer.cornerRadius = 2
self.view.addSubview(rippleView)
UIView.animateWithDuration(0.5, delay: 0.0, options: .CurveEaseInOut, animations: {
rippleView.transform = CGAffineTransformMakeScale(200.0, 200.0)
rippleView.bounds = bounds
}, completion: {
finished in
print(rippleView.frame.origin.x)
})
}
Currently the view just grows beyond the size of the screen.
The print statement returns -37176 instead of say 0. I want it to fill the screen and nothing more.
If you want it to fill a rectangle.
1) create a rectangular container UIView.
2) add your rippleView as a subview to this rectangular uiview.
set the clipsToBounds property to yes of your container view.
and just do the animation.
let rectView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100));
rectView.backgroundColor = UIColor.redColor()
// initialRippleView
let rippleView = UIView(frame: CGRect(x: 15, y: 15, width: 2, height: 2));
rippleView.backgroundColor = UIColor.whiteColor()
rippleView.cornerRadius = rippleView.width / 2;
rectView.addSubview(rippleView);
rectView.clipsToBounds = true;
UIView.animateWithDuration(5) { () -> Void in
rippleView.transform = CGAffineTransformMakeScale(100, 100);
}
Try in playground.
import UIKit
import XCPlayground
// the main View
let iPhone = UIView(frame: CGRect(x: 0, y: 0, width: 320, height: 568));
iPhone.backgroundColor = UIColor.greenColor();
// the rect View that will be the bounds of the animation
let rectView = UIView(frame: CGRect(x: 0, y: 0, width: 150, height: 150));
rectView.backgroundColor = UIColor.redColor()
rectView.center = iPhone.center;
// initialRippleView
let rippleView = UIView(frame: CGRect(x: 15, y: 15, width: 2, height: 2));
rippleView.layer.cornerRadius = 1;
rippleView.backgroundColor = UIColor.whiteColor()
iPhone.addSubview(rectView);
rectView.addSubview(rippleView);
// this property clips the drawings of subview to be clipped to the bounds of rectView.
rectView.clipsToBounds = true;
UIView.animateWithDuration(5) { () -> Void in
// you may need to calculate the right scale factor
rippleView.transform = CGAffineTransformMakeScale(200, 200);
}
// Playground stuff
XCPShowView("Container View", view: iPhone);
Copy this code in a playground File
Show the Assistant editor
Press play (in the left bottom corner)

How to CGPath or SKShapeNode Rotation

I want to add rotated elliptical paths to one center point. Like image below but random rotated elliptical.
Draw these elliptical orbits with,
roadShape = SKShapeNode(ellipseInRect: centeredRect)
roadShape.strokeColor = UIColor.greenColor()
self.parent!.addChild(roadShape)
need to make small particles follow those elliptical orbits, thats why I need a path.
var followPath = SKAction.followPath(roadShape.path, asOffset: false, orientToPath: true, duration: 10);
particle.runAction(followPath)
I used SKShapeNode to draw and get the path property. But unfortunately SKShapeNode doesn't have any origin or anchorPoint property so I couldn't decent rotate to SKShapeNode. Do you have any suggestion to make it rotate or different way.
Thanks in advance.
let roadShape1 = SKShapeNode(ellipseInRect: CGRectMake(-100, -25, 200, 50))
roadShape1.strokeColor = UIColor.greenColor()
roadShape1.lineWidth = 3
roadShape1.position = CGPoint(x:frame.midX, y: frame.midY)
addChild(roadShape1)
let roadShape2 = SKShapeNode(ellipseInRect: CGRectMake(-100, -25, 200, 50))
roadShape2.strokeColor = UIColor.greenColor()
roadShape2.lineWidth = 3
let action2 = SKAction.rotateByAngle(CGFloat(M_PI)*0.75, duration:0)
roadShape2.runAction(action2)
roadShape2.position = CGPoint(x:frame.midX, y: frame.midY)
addChild(roadShape2)
let roadShape3 = SKShapeNode(ellipseInRect: CGRectMake(-100, -25, 200, 50))
roadShape3.strokeColor = UIColor.greenColor()
roadShape3.lineWidth = 3
let action3 = SKAction.rotateByAngle(CGFloat(M_PI)*0.25, duration:0)
roadShape3.runAction(action3)
roadShape3.position = CGPoint(x:frame.midX, y: frame.midY)
addChild(roadShape3)
You can also use applyTransform to apply the rotation to the bezierPath
instead of the SKShapeNode as follow:
let roadPath1 = UIBezierPath(ovalInRect: CGRect(x: -100, y: -25, width: 200, height: 50))
let roadPath2 = UIBezierPath(ovalInRect: CGRect(x: -100, y: -25, width: 200, height: 50))
roadPath2.applyTransform(CGAffineTransformMakeRotation(45.0 * CGFloat(M_PI) / 180))
let roadPath3 = UIBezierPath(ovalInRect: CGRect(x: -100, y: -25, width: 200, height: 50))
roadPath3.applyTransform(CGAffineTransformMakeRotation(135.0 * CGFloat(M_PI) / 180))
let roadShape1 = SKShapeNode(ellipseInRect: CGRectMake(-100, -25, 200, 50))
roadShape1.path = roadPath1.CGPath
roadPath1.stroke()
roadShape1.lineWidth = 3
roadShape1.position = CGPoint(x:frame.midX, y: frame.midY)
addChild(roadShape1)
let roadShape2 = SKShapeNode(ellipseInRect: CGRectMake(-100, -25, 200, 50))
roadShape2.path = roadPath2.CGPath
roadPath2.stroke()
roadShape2.lineWidth = 3
roadShape2.position = CGPoint(x:frame.midX, y: frame.midY)
addChild(roadShape2)
let roadShape3 = SKShapeNode(ellipseInRect: CGRectMake(-100, -25, 200, 50))
roadShape3.path = roadPath3.CGPath
roadPath3.stroke()
roadShape3.lineWidth = 3
roadShape3.position = CGPoint(x:frame.midX, y: frame.midY)
addChild(roadShape3)

Generating a Bezier Square in Multiple Steps

I can generate a square with this standard code.
func drawSquare() {
let width:CGFloat = 100
var square = UIBezierPath()
square.moveToPoint(CGPoint(x: 0, y: 0))
square.addLineToPoint(CGPoint(x: width, y: 0))
square.addLineToPoint(CGPoint(x: width, y: width))
square.addLineToPoint(CGPoint(x: 0, y: width))
square.lineWidth = 5
square.closePath()
UIColor.blackColor().setStroke()
square.stroke()
}
However, if I want to draw each addLineToPoint() segment in two steps, my square no longer draws correctly.
func drawSquare2Steps() {
let width:CGFloat = 200
let mult:CGFloat = 2
var square = UIBezierPath()
square.moveToPoint(CGPoint(x: 0, y: 0))
square.addLineToPoint(CGPoint(x: 100, y: 0)) // 1
square.addLineToPoint(CGPoint(x: 200, y: 0)) // 2
square.addLineToPoint(CGPoint(x: 200, y: 100)) // 3
square.addLineToPoint(CGPoint(x: 200, y: 200)) // 4
square.addLineToPoint(CGPoint(x: 100, y: 200)) // 5
square.addLineToPoint(CGPoint(x: 0, y: 200)) //6
square.lineWidth = 5
square.closePath() // 7
UIColor.orangeColor().setStroke()
square.stroke()
}
Here's the path that I see:
I tried to describe this function in this image:
My final goal isn't to draw a square but a more complicated path. The square simply illustrates the problem with my complex path.
Nothing is wrong except that your view size is (100, 100) but the square size is (200,200), so you don't see the whole square.

Resources