Is it possible to create an instance of a particular location on the screen (using auto layout)? - swift

I have 4 playing cards on the screen. At the press of a button, I want one of the cards at random to move to the middle of the top half of the screen. Is it possible to create an instance of a constraint (eg: centerXAnchor with constant 0, and centerYAnchor with constant -200) so that I can use CGAffineTransform and move the random image to this point?
Ive tried creating an instance of a CGRect Frame:
let destination = CGPoint(x: 10, y: 10)
but this does not move evenly across devices.

An affine transformation matrix is used to rotate, scale, translate, or skew the objects you draw in a graphics context.
I don't think CGAffineTransform is the ideal thing to use for this task. You aren't doing any the above things (rotate, scale, translate, or skew).
I think you would likely be best using UIView.animateWithDuration
let cardSize = CGSize(width: 100, height: 100)
let card = UIView(frame: CGRect(origin: CGPoint(x: 0, y: 0), size: cardSize))
UIView.animate(withDuration: 1.0) {
card.frame = CGRect(origin: CGPoint(x: 100, y: 100), size: cardSize)
}

Related

SWIFT problems with CGAffineTransform... button always go to origin

I got a problem with the animations from CGAffineTransform... I want to move the button and scale it up at the same time.
But if I do one animation alone everything works pretty straight forward. It scales up or moves the button by 50px.
But if I put both in the same animation its messed up. The button starts from far outside the screen to move in and doesn't scale as it should.
Also, after the movement of the button. The buttons scales up to the correct size i expected, but moves back to origin position before...
What do I miss here?
let shape = CAShapeLayer()
//shape.path = UIBezierPath(arcCenter: CGPoint(x: 25, y: 25), radius: 30, startAngle: 0, endAngle: .pi/2, clockwise: true).cgPath
shape.lineWidth = 5
shape.strokeColor = UIColor.black.cgColor
timerButton.layer.addSublayer(shape)
UIView.animate(withDuration: 2, animations: {
self.timerButton.transform = CGAffineTransform(translationX: -50, y: 0)
//self.timerButton.transform = CGAffineTransform(scaleX: 3, y: 3)
}) { (true) in
UIView.animate(withDuration: 1, animations: {
self.timerButton.transform = CGAffineTransform(scaleX: 3, y: 3)
}) { (true) in
print("later")
}
}
At completion of the transform of the x position, you need to change the constraint to make it permanent, the top constraint, after that also in the completion you need to remove the x position transformation,
If you follow these steps and after them animate your new transformation it should work

Swift: Constrain object to and point within a circular NSLayoutConstraint? Circle anchor?

Within a view controller, I have a UIView (backgroundCircle) and a UILabel. The UILabel is moving based on motionManager.startAccelerometerUpdates data. Using this data, I am animating the label.center point. However, I want to bound the movement so that it only moves within a circular area around its starting point (as if it was on a leash from the starting point). Is this possible? Sample code below if it helps.
//objects called during set up
let backgroundCircle = UIView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
backgroundCircle.layer.cornerRadius = circle.frame.width / 2
backgroundCircle.backgroundColor = .clear
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
label.text = "Hello"
//this is called in a separate function, continuously
label.center = newCenter
I'd try using UIKit Dynamics, especially UICollisionBehavior which allows to define collision boundaries with bezier paths.

ARKit: Occlude everything in a plane except reference image

I have an image reference that is triggering an experience on ARKit2, and I want to create an occlusion plane that covers everything around my trigger, except the trigger itself. Representatively, it is something like this (placed the image below the plane to show better, but it is in reality on the same level):
As of now, I have no issue with making an occlusion on a regular shape like so:
func setOcclusionMaterial() {
let occlusionPlane = SCNPlane(width: imageReference!.physicalSize.width, height: imageReference!.physicalSize.height)
let holdout = SCNMaterial()
holdout.isDoubleSided = true
holdout.diffuse.contents = CIColor.black
holdout.colorBufferWriteMask = SCNColorMask(rawValue: 0)
occlusionPlane.firstMaterial? = holdout
let occlusionNode = SCNNode()
occlusionNode.eulerAngles.x = -.pi / 2
occlusionNode.geometry = occlusionPlane
node!.addChildNode(occlusionNode)
}
However, I'm not sure how I could create a plane that excludes the image area (a circle).
What I'm trying to do is to place a 3D element that appears to be below my trigger but only visible if seen top-down or with a slight angle.
Hope this is clear enough and thanks for your help if you have any ideas.
Your code does not render an occlusion for me, at least not in the Playground I quickly threw together. Maybe you can create one if my answer does not actually solve your problem.
You have to options here. Either create a custom geometry, in a 3D modelling tool like blender or manual via SCNGeometrySource but that is expansive because you will need a lot of triangles to make it work.
You can try to apply a CALayer as the plane's geometry:
Create a CGPath that is the rectangle with the circular cutout (or any form you'd like) and create a CAShapeLayer from the path.
var path = CGMutablePath()
path.addRect(CGRect(x: 0, y: 0, width: 100, height: 100))
path.addEllipse(in: CGRect(x: 25, y: 25, width: 50, height: 50))
let layer = CAShapeLayer()
layer.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
layer.path = path
layer.fillColor = UIColor.white.cgColor
layer.fillRule = .evenOdd
I could not confirm this actually worked because your occlusion is not quite working for me.

Filling animation + permanent mask in swift?

Something like this:
Or follow this link if you can:
https://www.lottiefiles.com/450-play-fill-loader
My case is simpler - I have a view with mask which consists of multiple rects and a linear fill. Mask is created in runtime (so I can't use lottie) but remains permanent, fill is animated (filling from right to left). But how to draw it?
Note: I tried to find a similar animation implementation but in most cases they just try to change the mask params while in my case mask is constant.
If i understand you correct, you can add subview under your mask and change its width, something like that:
let fillingStartFrame = CGRect.init(x: 0, y: 0, width: 0, height: view.frame.height)
let fillingEndFrame = CGRect.init(x: 0, y: 0, width: view.frame.width, height: view.frame.width)
let fillingView = UIView(frame: fillingStartFrame)
view.insertSubview(fillingView, belowSubview: YOUR_MASK)
UIView.animate(withDuration: 0.3) {
fillingView.frame = fillingEndFrame
}

Adding SKShapeNode as Child to SKSpriteNode

Using SpriteKit, I'm trying to create a rectangle as a background, with a second rectangle 25% of the size of the background and position it at the bottom of the background.
(so the lower 25% is blue, and the upper 75% is dark grey)
This is my set up:
screenWidth = size.width //312
screenHeight = size.height //390
func createSky(){
let sky = SKSpriteNode(color: SKColor.darkGray, size: CGSize(width: screenWidth, height: screenHeight))
print("Sky: ", sky.frame)
addChild(sky)
}
func createGround(){
let ground = SKShapeNode.init(rectOf: CGSize(width: screenWidth, height: screenHeight / 4))
ground.fillColor = SKColor.blue
print("Ground: ", ground.frame )
sky.addChild(ground)
}
For some reason, ground.frame is printing Ground: (-157.35, -50.1, 314.7, 100.2). Why is this? Shouldn't the last 2 figures be 312, 97.5?
I've tried various .positions and .anchorPositions but I still can't get the SKShapeNode to sit correctly within the SKSpriteNode.
Eventually the Shape will be more complex which is why I'm not using Sprite for the second one. What am I missing?
So what it looks like from your code is that you are trying to create a shapeNode and then position it inside your SKSpriteNode. Most of it you got right, you just did the positioning code incorrectly.
When you add a child to a node, that child uses the parent node as its origin point for reference. So if you tell the child node to be -50 X and 150 Y, that child node will be -50 X and 150 Y from the center point of its parent node.
When I do child nodes (especially ones that have a ton of similarities to the parent node) I like to reference the parent node for positioning, width, and height. I don't know if it might be bad practice, but its what I've done for two years now.
This is how I would have done it:
var sky = SKSpriteNode()
var ground = SKShapeNode()
func createSky() {
sky = SKSpriteNode(color: SKColor.darkGray, size: CGSize(width: self.frame.size.wdith, height: self.frame.size.height))
// self.frame.size.width/self.frame.size.height calls its info from the ViewController, which is tied directly to your screen size (unless otherwise changed)
sky.position = CGPoint(x: 0,y: 0)
self.addChild(sky)
}
func createGround() {
ground = SKShapeNode((rectOf: CGSize(width: sky.width, height: sky.height * 0.25))
ground.position = CGPoint(x: 0, y: sky.position.y - (ground.size.height / 1.5))
}