SKLabelNode showing on iPhone simulation but not iPad - ios

I have this code to put a score label for my game in the top left corner of the screen.
scoreLabel = SKLabelNode(text:"bats avoided: \(score)")
scoreLabel.fontSize = 50
scoreLabel.horizontalAlignmentMode = .left
scoreLabel.position = CGPoint (x:10 - (frame.size.width/2), y: (frame.size.height/2) - 50)
but the label appears on iPhone simulator only, not iPad.
If I print the portion of the label at runtime, it shows that the position is x: -365 y:617 with a screen width/2 of 375 and screen height/2 of 667, so it should definitely be there.
what gives?

The coordinate system in iOS represents 'top left' as 0,0. That is to say that as you increase x, you move to the right and as you increase y, you move down.
You are setting a value of 10 - (frame.size.width/2) for x. Suppose your frame.size.width is 300, for example, this would be equal to -140. Remember how I said 'top left' is represented as 0,0? Well, if you have an x coordinate of -140, the element will be further left than 0,0, and subsequently offscreen.
To place something in the top left, inset by 10 points. You would simply set it's position to be CGPoint(x: 10, y: 10).
It is worth noting that when working with sprites, as in your case, that Y0 is actually the bottom, rather than the top of the screen. This a somewhat annoying discrepancy but one to keep in mind.

Related

Animate Image Rotation of a Button

I'm creating a UIButton and adding it above a UITabBar, in a custom UITabBarController.
var button = new UIButton(new CGRect(x, y, 50, 50));
button.Layer.BorderWidth = 2;
button.Layer.CornerRadius = button.Frame.Height / 2;
button.SetImage(UIImage.FromBundle("Name"), UIControlState.Normal);
this.View.AddSubview(button);
this.View.LayoutIfNeeded();
When I try to add a rotation transform on the image view, it doesn't rotate around the center and z axis, as I'd expect (and found in every documentation I read).
Instead, it animates somehow different.
It's the same regardless if I add the transform on the ImageView or its layer.
This for example is a rotation of 30 degrees (pi / 180 * 30) and 10 degrees.
However, when I add a SublayerTransform to the button layer, it transforms as expected. The problem with that approach is that it is not animateable with UIView.Animate or CATransaction.Begin.
Position, AnchorPoint and everything else is set to default. I tried playing around with these properties too, but to no avail.
As you can see from the images, it's a + sign that should animate to become an x.
Any hint is very much appreciated.
The 3D transforms I have defined like this:
CATransform3D.MakeRotation((float)(Math.PI / 180 * 45), 0, 0, 1)
and the normal ones like this:
CGAffineTransform.MakeRotation((float)(Math.PI / 180 * 45))
If you need to rotate just the button image view for some reason, try this:
button.ImageView.Layer.Transform = CATransform3D.MakeRotation((float)(Math.PI / 180 * 45), 0.0001f, 0.0001f, 1);
With the x and y values set to 0, it seems that there is still rotation on the x and y axis, though there should not be. With a value of 45 degrees, it rotates the x and y as well so that you are seeing the image edge on (so seems to be rotating x and y by 90 degrees), i.e. it disappears. Putting in a small value for X and y seems to avoid this error. I am going to see if this is exclusive to Xamarin or if it is an iOS thing.
EDIT: I just tested this in Obj-C in XCode and got the exact same behavior, so this is not a Xamarin issue but is an issue with iOS and how it is processing transforms for the UIImageView that is in a UIButton. Same issue does not occur with a UIImageView that is not in the UIButton.

Scale UIVIew upwards

I have an UIView which I want to scale to double its size. I've tried these 2 commands:
self.bottomView.transform = CGAffineTransform(translationX: 0, y: -125)
This one moves to the point I want but it moves the entire view so I get a gap at the bottom.(125 is my original height)
self.bottomView.transform = CGAffineTransform(scaleX: 1, y: 2)
This one stretches the view but it stretches both ways, up and down. I want it to only stretch in an upward Y-axis direction and not to both ways.
Which one should I continue with? Is there any way to choose which way the view should stretch? Furthermore, scaleX: y: stretches the subviews as well which isn't optimal for my cause.
I think you can use the below API.
CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy)
Update only one axis: X or Y with recognizer.scale and keep the other one 1.0f to achieve one direction scale.
Here is a simple Solution that may help you -- Just Animate your stretching operation on UIView.
UIView.animate(withDuration: 0.2) {
self.bottomView.frame.size.height = self.bottomView.frame.size.height * 2
}

iOS constraint equation values

I'm trying to find out what the values of the left and right views in a constraint equation are.
Currently this is how I see it.
The origin-point (0,0) in the coordinate system is at the top left.
Therefore views.attribute that are closer to the top and left are smaller.
In the image posted above.
RedView.Leading has a higher value than BlueView.trailing.
The equation is satisfied because 8 is added to BlueView.trailing.
The same would apply to the circled constraint in the image below.
superView.top is less than greyView.top because superView.top is on origin.x .
My question is are the values relative to the origin point ?
Theory of Relativity in Auto Layout
Short answer:
Yes and no. Actually more no. But most importantly: It's irrelevant!
Detailed answer:
Layout attributes are abstract descriptions of a view's position and size.
Position attributes:
top
bottom
leading
trailing
...
Size attributes:
width
height
While size attributes can describe an absolute value (e.g. view.height = 20.0) position attributes are always relative to another position attribute. That's why Apple only shows two views in their example, without any coordinate system. The equation
RedView.leading = 1.0 × BlueView.trailing + 8.0
states that RedView's leading edge is always 8.0 points to the right of BlueView's trailing edge. The origin of the underlying coordinate system doesn't matter!
Let's say we have a coordinate system ∑1 with an origin O1 and let's assume that BlueView's trailing edge is at x = 100 with respect to that origin. This would mean:
BlueView.trailing = 100
RedView.leading = 1.0 × 100 + 8.0 = 108
Now we look at a different coordinate system ∑2 with an origin O2 that's shifted by 20 points to the left, so
O2.x = O1.x – 20
O2.y = O1.y
In this coordinate system BlueView's trailing edge is at x = 120. So we get:
BlueView.trailing = 120
RedView.leading = 1.0 × 120 + 8.0 = 128
As you can see the values for the layout attributes BlueView.trailing and RedView.leading are different in ∑1 and ∑2. However, the horizontal spacing between the views is the same
RedView.leading – BlueView.trailing = 8
in both coordinate systems.
And that's the whole point of Auto Layout:
To describe the positions and sizes of views relative to each other, rather than using absolute values with respect to a particular coordinate system.
When I tell you to park your car behind your neighbor's car and leave a 1 meter gap in between, you know what to do, right? Without knowing where the road begins!
It's not important.
However – and I guess that's what made you ask the question – the system will need to "tell" the display at some point which pixels to draw for a particular view. And the pixel grid does have an absolute origin and a fixed coordinate system.
So eventually, the system will substitute the layout attributes for the outermost view (the window) before solving all the constraint equations. At that point in time your layout attributes will be relative to a particular origin (most likely the window's origin in the upper left corner, yes) but it's simply irrelevant!
Apple may choose any coordinate system they want (even a coordinate system whose origin is 50 points above the screen) and regardless of that particular system your layout will still look the same with the same set of constraints.
No, values are not relative to origin point. Forget about this.
To position them there must be some additional constraints applied to such attributes of views as:
left, right, top, bottom, leading, trailing, width, height, centerX, centerY, lastBaseline, firstBaseline, leftMargin, rightMargin, topMargin, bottomMargin, leadingMargin, trailingMargin, centerXWithinMargins, centerYWithinMargins.
Also in iOS 9 there were added diffrent kind of anchorPoints to make adding constraints easier.
Also Autolayout added localized leading and trailing attributes which position (leading is at left or right side of view) depends on Device Locale.
I would suggest the following equations:
redView.width = 0 + 1 * blueView.width
redView.height = 0 + 1 * blueView.height
redView.leading = 20 + superView.leading
blueView.trailing = -20 + superView.trailing
redView.bottom - blueView.bottom
redView.bottom = superview.bottom - 20
So it does not matter where origin is.
Everything you are asking requires knowledge of Auto Layout.
Leading, Trailing, Top, Bottom and other several constraints are applied w.r.t to the views.
Example:
RedView.leading = 1.0 x BlueView.trailing + 8.0
here, the leading constraint of RedView is applied w.r.t the BlueConstraint trailing whatever it is. i.e. RedView is placed 8 points farther than BlueView in horizontal direction.
https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/index.html
provides a good knowledge of the auto layout constraints, in what context they are applied and the how the views layout according to them.
Also there are top layout guide, bottom layout guide, margins with respect to which you apply constraints to a view.
Read more about auto layout to get a clear understanding.
Edit:
Example:
BlueView frame: (x: 0, y: 0, width: 4, height: 2)
Now the BlueView trailing that we have is: 4
So now we are setting RedView leading as:
RedView.leading = 1.0 x BlueView.trailing + 8.0
i.e. RedView.leading = 1.0 x 4 + 8.0 = 12.0
So now the frame of RedView is: (x: 12, y: 0, width: 4, height: 2)
Also from above equation,
BlueView.trailing = RedView.leading - 8.0
i.e., BlueView.trailing = 12.0 - 8.0 = 4.0
So, the equation is valid for both RedView and BlueView.

Using CATransform3DRotate with perspective: how to correct the 2D size increase?

I'm trying to create a paper folding effect in Swift using CALayers and CATransform3DRotate. There are some libraries out there, but those are pretty outdated and don't fit my needs (they don't have symmetric folds, for example).
My content view controller will squeeze to the right half side of the screen, revealing the menu at the left side.
Everything went well, until I applied perspective: then the dimensions I calculate are not correct anymore.
To explain the problem, I created a demo to show you what I'm doing.
This the content view controller with three squares. I will use three folds, so each square will be on a separate fold.
The even folds will get anchor point (0, 0.5) and the odd folds will get anchor point (1, 0.5), plus they'll receive a shadow.
When fully folded, the content view will be half of the screen's width.
On an iPhone 7, each fold/plane will be 125 points unfolded and 62.5 points fully folded when looked at.
To calculate the rotation needed to achieve this 62.5 points width, we can use a trigonometric function. To illustrate, look at this top-down view:
We know the original plane size (125) and the 2D width (62.5), so we can calculate the angle α using arccos:
let angle = acos(width / originalWidth)
The result is 1.04719755 rad or 60 degrees.
When using this formula with CATransform3DRotate, I get the correct result:
Now for the problem: when I add perspective, my calculation isn't correct anymore. The planes are bigger. Probably because of the now different projection.
You can see the planes are now overlapping and being clipped.
I reconstructed the desired result on the right by playing with the angle, but the correction needed is not consistent, unfortunately.
Here's the code I use. It works perfectly without perspective.
// Loop layers
for i in 0..<self.layers.count {
// Get layer
let layer = self.layers[i]
// Get dimensions
let width = self.frame.size.width / CGFloat(self.numberOfFolds)
let originalWidth = self.sourceView.frame.size.width / CGFloat(self.numberOfFolds)
// Calculate angle
let angle = acos(width / originalWidth)
// Set transform
layer.transform = CATransform3DIdentity
layer.transform.m34 = 1.0 / -500
layer.transform = CATransform3DRotate(layer.transform, angle * (i % 2 == 0 ? -1 : 1), 0, 1, 0)
// Update position
if i % 2 == 0 {
layer.position = CGPoint(x: (width * CGFloat(i)), y: layer.position.y)
} else {
layer.position = CGPoint(x: (width * CGFloat(i + 1)), y: layer.position.y)
}
}
So my question is: how do I achieve the desired result? Do I need to correct the angle, or should I calculate the projected/2D width differently?
Thanks in advance! :)

Autolayout center y alignment strange behaviour when setting multiplier

I have a helper view with dynamic height in my storyboard, this is a common practice to make layout responsive.
However, something strange happens when I introduce multipliers.
The blue button is aligned center y to the white view on the left:
Changing multiplier value into 0.5 should align the button to center of first half of the white view, at least it works this way when aligning to superview.
Instead I end up with sth like this:
The blue button height is equal to 0.05 times th height of the superview. The white view height is equal to 4 timees the height of the blue button
I have no idea what is the problem here that causes this weird alignment. I suspected it might be something with dynamic height value, so I tried setting explicit height value but the result was exactly the same.
You said that if button is Center Y with multiplier 0.5, than it should be positioned at the 1/4 of that white view.. No thats not worked like that .. lets check it with equation
multiplier works with this equation
FirstItem.Attribute1 = (SecondItem.Attribute2 * Multiplier) + Constant
Your constraint is Button.Center Y = BlankView.center Y .. so this is how equation filled up
Button.Center Y = (BlankView.center Y * 1) + 0
So the question is what is the value of BlankView.Center Y ...
answer is
BlankView.Center Y = HeightOfSuperviewOFBlankView - (Y positionOFBlankView + (BlankViewHeight / 2))
// in your case it would be 603 - (483 +(120/ 2)) = 543
Now move to Your equation Center Y with multiplier 0.5
Button.Center Y = (543 * 0.5) + 0 // 271.5
because of that your button position with Center Y is placed at
Button.Center Y = 271.5 - (buttonHeight / 2)
// if we take buttonHeight = 30 than it should be 257.5 (approx 257)
I hope now you understand how center Y with multiplier works...

Resources