Positioning a view after transform rotation - ios

I'm creating a custom popover background, so I subclassed the UIPopoverBackground abstract class. While creating the layout function I came across a problem with placing and rotating the arrow for the background.
The first picture shows the arrow at the desired position. In the following code I calculated the origin I wanted but the rotation seemed to have translated the new position of the image off to the side about 11 points. As you can see, I created a hack solution where I shifted the arrow over 11 points. But that still doesn't cover up the fact that I have a gapping hole in my math skills. If someone would be so kind as to explain to me what's going on here I'd be eternally grateful. What also would be nice is a solution that would not involve magic numbers, so that I could apply this solution to the cases with the up, down and right arrow
#define ARROW_BASE_WIDTH 42.0
#define ARROW_HEIGHT 22.0
case UIPopoverArrowDirectionRight:
{
width -= ARROW_HEIGHT;
float arrowCenterY = self.frame.size.height/2 - ARROW_HEIGHT/2 + self.arrowOffset;
_arrowView.frame = CGRectMake(width,
arrowCenterY,
ARROW_BASE_WIDTH,
ARROW_HEIGHT);
rotation = CGAffineTransformMakeRotation(M_PI_2);
//rotation = CGAffineTransformTranslate(rotation, 0, 11);
_borderImageView.frame = CGRectMake(left, top, width, height);
[_arrowView setTransform:rotation];
}
break;

Well, if the rotation is applied about the center of the arrow view (as it is), that leaves a gap of (ARROW_BASE_WIDTH - ARROW_HEIGHT) / 2 to the post-rotation left of the arrow, which is what you have to compensate for, it seems. By offsetting the center of the arrow view by this much, it should come back into alignment.

Related

Swift sprite kit vertical background infinite image

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.

Setting UIView.transform to arbitrary translate CGAffineTransform does nothing

I have a UIView called container that I want to move (offset) using affine transfrom. This view contains UIImageView and is a subview of UICollectionViewCell.
So it should be simple:
container.transform = CGAffineTransformMakeTranslation(100, 200) //render container 100 points right and 200 points down
Instead it is very hard, because theat code does not do anything. The view is rendered excatly on the same place as if I delete that line. So I added 'print' to verify what affine translation was set:
container.transform = CGAffineTransformMakeTranslation(100, 200)
print(container.transform) //prints: CGAffineTransform(a: 1.0, b: 0.0, c: 0.0, d: 1.0, tx: 100.0, ty: 200.0)
That seems all right. So I tried rotating the container view instead with CGAffineTransformMakeRotation and it rotates the view just not around its center as it should according to documentation. I tried different combinations of translate, rotation and scale transforms just to find that the affine transformation matrixes set are OK, but attributes tx and ty seems to be ignored and a, b, c and d seems to be using different anchor point then the centre of the view (cannot say what that point is).
Any ideas on what can be causing this and how to fix it?
There must be something like auto layout messing things up for you. In the absence of outside influence, setting a view's affine transform to CGAffineTransformMakeTranslation(100, 200) will shift it right 100 points and down 200. I verified this by making a new Single View Project in Xcode and changing the viewDidLoad method in the ViewController.swift class to:
override func viewDidLoad()
{
super.viewDidLoad()
view.backgroundColor = UIColor.blueColor();
let container = UIView(frame: CGRectMake(0,0,100,100));
container.backgroundColor = UIColor.greenColor();
container.transform = CGAffineTransformMakeTranslation(100, 200);
view.addSubview(container);
}
As expected this makes the green container view appear 100 points to the right and 200 points down, even though its frame is (0,0,100,100).
So please check for auto layout and other such things that might influence the placement of this view, and if you can't find anything please post more code. Also, if your container view doesn't have a background color, please give it one so that you can see its position directly, instead of deducing its position by looking at the image view.
n.b. Setting a view's transform doesn't actually move the view itself, it just changes how/where it draws its content.

Swift: Which direction a UIView is being intersected from?

I am using CGRectIntersectsRect to test if blueView (being dragged) has intersected with redView (stationary). However, I need to know if redView was intersected by blueView from red's top, bottom, right or left?
Is there a CG method to accomplish this?
I'm not aware of any CG Method to accomplish this.
The way I'd go about this is using CGRectIntersectsRect to check if they intersect at all, then if they do intersect, measure from the center of redView to blueView which would return the distance between them (X and Y), from that you could possibly tell from which angle the intersection was coming from?
If you wanted a little more accuracy you could measure from redView top center, to blueView bottom center, and the same for all 4 edges? Then work out which is the closest?
There are multiple ways you could approach this problem but I'm not aware of a 'one method fits all' solution.
Edit:
Was still thinking about this, I think the most accurate way would be something like this:
let leftDistance = blue.origin.x - (red.origin.x + red.width)
let rightDistance = (blue.origin.x + blue.width) - red.origin.x
let topDistance = blue.origin.y - (red.origin.y + red.height)
let bottomDistance = (blue.origin.y + blue.height) - red.origin.y
The the lowest of those distance = coming from the left, right, top or bottom.
(P.s. I might have gotten blue + Red the wrong way around, as well as top and bottom. I'd recommend just having a play around and logging these values while dragging the item)
Here is the solution I finally used, In case someone else is looking for it. Below line of code test if the intersection is vertical (top & bottom views intersected), where a and b are view.frame for each of the two view intersecting.
if ((a.maxY > q.maxY) && (b.minY < q.minY)) || ((a.maxY < q.maxY) && (b.minY > q.minY))

Collision detection not working on all sides of UIImageView

This code is used to test my collisions but it only works on 2 sides on each UIImageView. Why doesn't it work for 3 sides? It'll never work on top side and bottom side of UIImageView or left and right. It only works top and left or top and right or bottom and right or bottom and left. Why is that? How do i make it work on at least 3 sides?
int moveX;
int moveY;
viewController.h{
IBOutlet UIImageView *circle;
NSTimer *time;
IBOutlet UIImageView *platform;
}
-(IBAction)StartGame:(id)sender;
-(void)moveCircle;
-(void)bounce;
#end
viewcontroller.m
-(IBAction)StartGame:(id)sender{
start.hidden=YES;
time=[NSTimer scheduledTimerWithTimeInterval:0.02 target:self selector:#selector(moveCircle) userInfo:nil repeats:YES];
circleX=arc4random()%11;
circleX=circleX-5;
circleY=arc4random()%11;
circleY=circleY-5;
circleX=arc4random()%11;
circleX=circleX+5;
circleY=arc4random()%11;;
circleY=circleY+5;
if (circleX==0) {
circleX=1;
}
if (circleY==0) {
circleY=1;
}
-(void)moveCircle{
circle.center=CGPointMake(circle.center.x -circleX, circle.center.y -circleY);
if (circle.center.x <30) {
circleX=0-circleX;
}
if (circle.center.x >320) {
circleX=0-circleX;
}
if (circle.center.y <28 ) {
circleY=0-circleY;
}
if (circle.center.y >568) {
circleY=0-circleY;
}
[self bounce];
}
-(void)bounce{
if (CGRectIntersectsRect(circle.frame, platform.frame)) {
circleY=arc4random()%5;
circleY=0+circleY;
circleX=arc4random()%5;
circleX=0+circleX;
circleY=arc4random()%5;
circleY=0-circleY;
circleX=arc4random()%5;
circleX=0-circleX;
}
}
I see no sense at all in your question but let's look at your code:
Assuming you start with StartGame which hopefully happens only once:
You create a timer (this should be invalidated when the game ends but no matter at the moment)
you set circleX and circleY to some random position between -5 and 5
you set circleX and circleY to some random position between 5 and 15 (again?!)
you check if circleX and circleY are at 0 and set them to 1 (this is highly unlikely to happen due to the previous statement)
Then move circle:
you add to the circle center the circleX and circleY values where then I assume those 2 values are actually speed. (so far so good)
you check the circle position to be out of borders and negate appropriate speed. (This hardcoded values are a bit strange but ok for now I guess)
You call bounce
And the bounce method:
You check if circle and platform frames intersect. (O.K. I guess)
You set a random speed to circleX and circleY from 0 to 4
You set a random speed to circleX and circleY from 0 to -4 (again?!)
So what I can assume from this code is you have 2 views on your view controller where one is a static obstacle called platform and the other is a moving circle. The circle itself will be moving to some direction depending on some random values from the StarGame method and correctly bouncing off the hardcoded borders. If the circle intersects with the platform the circle will change speed to some random negative values from -4 to 0 in both X and Y.
So what is the issue here exactly? The bouncing off the platform will make the circle move toward your top-left corner of the screen no matter which side does the circle hit the platform from. Maybe you need some if statements in your bounce method to find a correct value for the new speed depending on which side is the platform hit from.

How do i resize an object created in actionscript with the point of origin staying central

widthEssentially, i am creating objects in Flash using actionscript. I currently have a problem where i am resizing the object in actionscript but as the point of reference seems to be the top left hand corner, the object when shrinking seems to also move towards the top left of the screen.
Is there a way to either code the point of origin for an object to the center of the object using code or to have it resize without seemingly moving?
my code for creating and resizing an object is like this (it will grow and shrink but at moment im just tryint to fix this problem before i move on):
var blob1:Loader = new Loader();
blob1.x = 1000;
blob1.y = 450;
blob1.load(new URLRequest("blob1.png"));
addChild(blob1);
blob1.addEventListener(Event.ENTER_FRAME, blobTween);
function blobTween(event:Event)
{
var size = 0;
if (size == 0){
blob1.width += 5;
blob1.height += 5;
}else if (size == 1){
}
Can't you update x and y while you change width and height ?
blob1.x += deltaWidth / 2
blob1.y += deltaHeight / 2
You increment width and height by 5, so deltaWidth = 5 and deltaHeight = 5 .
NOTE: deltaWidth / 2 doesn't divide equally, so you'll have slight shift. I suggest having deltaWidth = 6 and deltaHeight = 6 , or something similar.
GreenSock has a great library for doing just that; TransformManager.
http://www.greensock.com/transformmanageras3/
my solution in the end was a bit strange. I took the shape, divided it into 4 quarters and reattached them in flash by placing the objects in the same place and rotating them (as it rotates from the top left corner, the 4 quarters form a complete shape with each quarter having their top left corner in the centre of the "object"). I then resized them all at once to do it properly.
Whats weird is that the top right and bottom left corners required different math from the other two corners. Which makes no sense whatsoever. 2 corners required width and height to be adjusted. The other two corners required just width to be adjusted. very strange but worked :/

Resources