I am trying to create a parallax effect such as the iPhone homescreen where the background moves as your tilt your phone. I have so far achieved this, but I have one problem still. After I tilt my phone and the background moves, it very slowly moves back into position of being centered.
It's not the constraints. I removed the center x/y constraint and it still slid back slowly as if it was re-calibrating to the new position. The only other constraint is the ratio. So it's not the constraints.
any ideas?
The code is simple:
let leftRightMin = CGFloat(-50.0)
let leftRightMax = CGFloat(50.0)
let upDownMin = CGFloat(-35.0)
let upDownMax = CGFloat(35.0)
let leftRight = UIInterpolatingMotionEffect(keyPath: "center.x", type:
UIInterpolatingMotionEffectType.TiltAlongHorizontalAxis)
leftRight.minimumRelativeValue = leftRightMin
leftRight.maximumRelativeValue = leftRightMax
let upDown = UIInterpolatingMotionEffect(keyPath: "center.y", type:
UIInterpolatingMotionEffectType.TiltAlongVerticalAxis)
upDown.minimumRelativeValue = upDownMin
upDown.maximumRelativeValue = upDownMax
let fxGroup = UIMotionEffectGroup()
fxGroup.motionEffects = [leftRight, upDown]
backgroundImage.addMotionEffect(fxGroup)
Any ideas why it slowly centers the image back after tilting and how to fix it?
This is the behavior of UIInterpolatingMotionEffect. You'll notice it also happens on the home screen, and everywhere else in the system that the effect is used.
It does this because sometimes the user will move their device in such a way that the content interpolates to the maximum position, but does not return to the center or resting position. This means that the position that was once the maximum interpolating position is now the resting center, so the content must move back to its original position in case the device moves again and the effect can continue.
I did not find any mention of this behavior in the UIInterpolatingMotionEffect documentation, but it can be observed everywhere the system uses the effect.
Related
How to add text to screen space in RealityKit/SwiftUI?
All the tutorials online are about adding text to screen space with UIKit/Scene Kit, but it never uses SwiftUI/RealityKit. Even Apple's uses UIKit for most of their examples!
Apple's example of screen space
It's not a straightforward conversion from Apple's explain into SwiftUI/RealityKit. Apple would get the 2d screen point, but then update the UIText's view frame with that 2d screen point.
To put the annotation in the right place on the screen, ask the ARView
to convert its entity’s world location to a 2D screen point.
guard let projectedPoint = arView.project(note.position) else { return }
All I have right now is a tap handler that would raycast to the tap location. From that I have world coordinates, which I use for my anchor position. I created a mesh of text, but when adding to the anchor, it exists in world space and isn't aligned properly
let raycastResults = self.raycast(from: tapLocation, allowing: .estimatedPlane, alignment: .any)
guard let raycastFirstResult: ARRaycastResult = raycastRes.first else { return }
let position = raycastFirstResult.worldTransform
let mesh = MeshResource.generateText("Test")
Has anyone added text to world space using SwiftUI/RealityKit before?
I have a uislider that has a callout bubble whose center X follows above the center X of the uislider thumb.
I tested it on an iPhone 7 and it works perfectly, and it also works perfectly on other devices once the thumb is moved.
The issue I have is that on initial load, the bubble and thumb should be aligned as it does when the thumb is moved, but on a device like an iPhone 8+ or iPhone SE, the bubble's center x is off from the thumb's center x (+/- 20-ish pts).
I believe the calculation to get the two objects aligned is correct since it works fine when you actually move the thumb, but something with the initial calculation of the thumb's x is off. With some console logs, the thumb's X would initially read as 180 pts but on touch the first value is actually 201 pts. The bubble then adjusts accordingly.
I am using a custom uislider subclass for gradients and such, and that could potentially be affecting it(?) but it's confusing how it still works on touch.
Any help would be appreciated.
On viewDidLoad (leading constraint is to safe area):
distanceSlider.setValue(Float(sliderValue), animated: false)
distanceCalloutViewLeadingConstraint.constant = distanceSlider.thumbCenterX - (distanceCalloutView.center.x - distanceCalloutView.frame.minX)
On distance change:
distanceCalloutViewLeadingConstraint.constant = distanceSlider.thumbCenterX - (distanceCalloutView.center.x - distanceCalloutView.frame.minX)
Thumb center X extension:
extension UISlider {
var thumbCenterX: CGFloat {
let trackRect = self.trackRect(forBounds: frame)
let thumbRect = self.thumbRect(forBounds: bounds, trackRect: trackRect, value: value)
return thumbRect.midX
}
}
Try setting below line in viewDidLayoutSubviews(). It looks like the view in with slider is added is not properly layout its size.
distanceCalloutViewLeadingConstraint.constant = distanceSlider.thumbCenterX - (distanceCalloutView.center.x - distanceCalloutView.frame.minX)
Why are you using custom image and adjusting according to slider value. You can directly use
func setThumbImage(_ image: UIImage?,
for state: UIControl.State)
check the reference apple doc link https://developer.apple.com/documentation/uikit/uislider/1621336-setthumbimage
I have 3 images:
topBg.png
midBg.png
botBg.png
I want to set topBg.png at top scene and height = 200
middleBg.png should be infinite scale or repeat vertically
botBg.png - should be in bottom and height = 200
i have next code:
override func didMove(to view: SKView) {
self.bgTopSpriteNode = self.childNode(withName: "//bgTopNode") as? SKSpriteNode
self.bgMiddleSpriteNode = self.childNode(withName: "//bgMiddleNode") as? SKSpriteNode
self.bgBottomSpriteNode = self.childNode(withName: "//bgBottomNode") as? SKSpriteNode
if let bgTopSpriteNode = self.bgTopSpriteNode,
let bgMiddleSpriteNode = self.bgMiddleSpriteNode,
let bgBottomSpriteNode = self.bgBottomSpriteNode {
bgTopSpriteNode.size.width = self.frame.width
bgTopSpriteNode.size.height = 200
bgTopSpriteNode.position.x = 0
bgMiddleSpriteNode.size.width = self.frame.width
bgMiddleSpriteNode.size.height = self.frame.height-400
bgMiddleSpriteNode.position.x = 0
bgBottomSpriteNode.size.width = self.frame.width
bgBottomSpriteNode.size.height = 200
bgBottomSpriteNode.position.x = 0
}
}
But how to set Y position of images. Because coordinates begin from center of screen, not from left top and i don't know how to convert them.
There are a couple of different ways to achieve what you're looking to do.
First, you can compute the y position of the top and the bottom of the screen using simply size.height / 2 if you have the anchorPoint of your scene at (0.5,0.5). (Don't use frame - use size. That way, you take into account the scaleMode of the scene.)
It sounds like you are frustrated that the origin of the scene is in the center. If you'd like to move it to the corner, you can easily do so by setting the scene's anchorPoint property, say, to (0.0, 0.0) for the lower left corner. Then, your y-values are 0 and size.height. If you are using the .sks editor, this is exposed in the interface - you can just set it there. Otherwise, you can set it programmatically.
Finally, you can set the scaleMode of your scene to something like .aspectFill, set the size of the scene directly (say, to 1024x768 for an iPad), and just place the images wherever they need to go. This approach works particularly well with .sks files, if you are using them; when you load up a scene, you can set the size of the scene based on the aspect ratio of the view it's in to accommodate different aspect ratios. For instance, you could adopt a 320x480 "reference size" for your iPhone scenes. Whenever you load up the scene, you could set the size of the scene to be 320 points wide and however many points tall to match the aspect ratio of the device. Then, all your graphics would be produced at 320pt wide, and you could slide them up or down proportionally across the scene's size for layout. This is a little more complicated, but it's a lot easier than trying to deal with separate layout considerations for multiple devices.
I should also point out a couple of things.
You can use the anchorPoint property of a sprite to dictate where the sprite's coordinates are measured from. This is handy for cases where you want images to be flush up against something. For instance, if you want an image flush against the left side of the screen, set its position to be exactly the left side of the screen, and then set its anchorPoint.x to 0.0; this will put the left edge of the sprite against the left edge of the screen. This also works for scenes, as you encountered - moving the anchorPoint of the scene moves everything in the scene relative to its size.
You don't need three images for what you're describing. You can use a single sprite and just set its centerRect property to tell it to use the top and bottom of an image and stretch the center part vertically. You have to do a little math to set the right xScale and yScale (not width and height, IIRC), but then you can draw all of that with one sprite instead of three. This would be really handy in your case, because you could just leave the sprite at (0,0), set its scale to match the size of the entire scene, and set the centerRect property - you wouldn't have to do any positioning math at all.
I'm trying to make a game where the sprite will always move to the right when hit by an object. However since the Sprite rotates constantly and the zero radians rotates with the Sprite causes my calculated magnitude to go the opposite direction if the sprite is facing left and hits the object. Is there a way to keep the direction of the magnitude always pointing to the right even if the zero is facing left?
// referencePoint = upper right corner of the frame
let rightTriangleFinalPoint:CGPoint = CGPoint(x: referencePoint.x, y: theSprite.position.y)
let theSpriteToReferenceDistance = distanceBetweenCGPoints(theSprite.position, b: referencePoint)
let theSpriteToFinalPointDistance = distanceBetweenCGPoints(theSprite.position, b: rightTriangleFinalPoint)
let arcCosineValue = theSpriteToFinalPointDistance / theSpriteToReferenceDistance
let angle = Double(acos(arcCosineValue))
let xMagnitude = magnitude * cos(angle)
let yMagnitude = (magnitude * sin(angle)) / 1.5
Not sure if this works for you:
I would use an orientation constraint to rotate the sprite. The movement can be done independent from the orientation in that case.
I made an tutorial some time ago: http://stefansdevplayground.blogspot.de/2014/09/howto-implement-targeting-or-follow.html
So I figured out what was going on.
It seems like the angle doesn't rotate with the Sprite like I originally thought and the vector that I am making is working with the code above. THE problem that I had was that I also set the collision bit for the objects which is wrong. If I only set the contact bit for the objects against the sprite the my desired outcome comes true.
I have a UIScrollView that when I tilt the iPhone, I want the view to scroll. Please note that I want to scroll two dimensionally, upwards and side to side. I've looked everywhere for answers, Like this. Here's some code:
self.motionManager.startDeviceMotionUpdatesToQueue(NSOperationQueue.mainQueue(), withHandler: {
(motion:CMDeviceMotion!, error:NSError!) in
let xRotationRate = motion.rotationRate.x
let yRotationRate = motion.rotationRate.y
let zRotationRate = motion.rotationRate.z
let xoffset = CGFloat(self.scrollView.contentOffset.x) + CGFloat(xRotationRate)
let contentOffset:CGPoint = CGPointMake(offset, 0)
self.scrollView.setContentOffset(contentOffset, animated:true)
})
Even with this extremely simplified code, I can't get feedback on my phone that is consistent with the rotation of the device. All the views in the scroll view just seem to slowly move to the top of the screen. How would you scroll two dimensionally by tilting the device.