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

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.

Related

UIView does not change colors after setting backgroundColor programmatically

I'm creating a subview to add onto an existing view. I'm trying to assign the background color to this subview to red programmatically but displays as the defaulted color still.
let toggleView = UIView(frame: CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight))
toggleView.backgroundColor = UIColor.red
As from the above code snippet it looks like that you are creating a view but not adding it as a subview to the parent view. It will be best if you can provide full function so that we can look into the actual cause.
You have created the toggleView with a red background but you have not added it to the main view, this is what you should do:
let screenWidth = view.frame.size.width
let screenHeight = view.frame.size.height
let toggleView = UIView(frame: CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight))
toggleView.backgroundColor = UIColor.red
view.addSubview(toggleView)

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

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)
}

How to position an SKLabelNode on top of a UISlider in SpriteKit

I'm using UISliders in my SpriteKit game.
Sometimes I want to pause the game and add some info in an SKLabelNode but I can't find a way to position the SKLabelNode on top of a UISlider.
Here's a simplified example.
override func didMove(to view: SKView) {
let slider = UISlider(frame: CGRect(x: 100, y: 100, width: 300, height: 50))
let label = SKLabelNode(text: "Text Label should be on top of Slider")
label.fontColor = .red
label.verticalAlignmentMode = .top
label.position = CGPoint(x:160, y:self.frame.size.height - 110)
view.addSubview(slider)
self.addChild(label)
slider.layer.zPosition = 1
label.zPosition = 2
}
UISlider remains above SKLabel :-(.
Any help appreciated.
EDIT:
In response to question by #Muffinman2497 "Why not use a UILabel?"
I may have to do that but I'm actually using a custom multi line label that's been designed using spriteKit and SKLabels. So I was hoping for a simple property change rather than a redesign.
Using a UILabel definitely does work though.
let myUILabel = UILabel(frame: CGRect(x: 50, y: 0, width: 300, height: 50))
myUILabel.text = "UILabel would like to obscure UISlider"
myUILabel.backgroundColor = .red
slider.addSubview(myUILabel)
slider.layer.zPosition = 1
myUILabel.layer.zPosition = 2

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
}

UIView Orientation change in Swift

I'm developing an app to display a binary tree.
Each node will be displayed as a subview programatically generated from the ViewController - I run the following from viewDidLayoutSubviews().
let theView = BinaryTreeView(frame: CGRect(x: 0, y: 50, width: width, height: 100))
// let theView = BinaryTreeView(s: "I'm testing")
theView.backgroundColor = .white
theView.addGestureRecognizer(UIPinchGestureRecognizer(target:theView, action:#selector(BinaryTreeView.changeScale(recognizer:))))
self.view.addSubview(theView)
theView.eyesOpen = false
let secondView = BinaryTreeView(frame: CGRect(x: width/2, y: 150, width: width/2, height: 100))
// let theView = BinaryTreeView(s: "I'm testing")
secondView.backgroundColor = .white
secondView.addGestureRecognizer(UIPinchGestureRecognizer(target:secondView, action:#selector(BinaryTreeView.changeScale(recognizer:))))
self.view.addSubview(secondView)
let thirdView = BinaryTreeView(frame: CGRect(x: (width/2)+width/4, y: 250, width: width/4, height: 100))
// let theView = BinaryTreeView(s: "I'm testing")
thirdView.backgroundColor = .white
thirdView.addGestureRecognizer(UIPinchGestureRecognizer(target:thirdView, action:#selector(BinaryTreeView.changeScale(recognizer:))))
self.view.addSubview(thirdView)
The issue is that on orientation change the views repeat each other (above there are three nodes, on orientation change 4 might display.
I looked through Stack and Within my subclassed UIView I added:
self.contentMode = UIViewContentMode.redraw
Within the programatic Subview but the same happens.
Don't worry - I'm going to generate my nodes in a loop later (I'm trying to understand how layout works). Incidentally I found the same happened using a UICollectionView so I seem to be doing something fundamentally wrong.
Change the "target:" to the controller (to self) for all three. Your controller will respond to gestures, not the views themselves. The target will be the same in all three cases.
secondView.addGestureRecognizer(UIPinchGestureRecognizer(target:secondView, action:#selector(BinaryTreeView.changeScale)))
self.view.addSubview(secondView)
becomes
secondView.addGestureRecognizer(UIPinchGestureRecognizer(target:self, action:#selector(BinaryTreeView.changeScale)))
self.view.addSubview(secondView)