I've run into an issue that is driving me nuts... I know it must be a simple fix, but I cannot find anything online (but !might be looking in the wrong places).
I have a bunch of buttons that I place in the view with a loop:
//make the button smaller and position
CGRect buttonFrame = button.frame;
buttonFrame.size = CGSizeMake(41, 41);
buttonFrame.origin = CGPointMake(10+(43*i), 415);
button.frame = buttonFrame;
This worked great...but now I want to add them into another view and they disappear. The offending line of code is:
buttonFrame.origin = CGPointMake(10+(43*i), 415);
I believe that this must be working off of the coordinates of self.view and not the new subview that holds the buttons. Do I need a new CGContext for the super view.
I tried something like:
buttonFrame.origin = [newView CGPointMake(10+(43*i), 415)];
But that caused all sorts of errors.
Thanks for your time!
The origin of the CGRect that is assigned as the frame of a view is the offset that the view will be placed at from it's superview.
So if you use a CGRect of (100, 100, 200, 200), the view will be 100 pixels to the right of the origin, and 100 pixels south (also remember that the iOS view coordinate system origin is in the top left corner) and has a size of 200x200.
Your button will be 415 pixels below the local origin (top left corner) of it's superview, which may place it out of view.
buttonFrame.origin = [newView CGPointMake(10+(43*i), 415)]; - this line of code has no sense, because CGPointMake is the C function, and not the ObjectiveC method, so you can't post it like the message to object.
If you want to add subview you have to use [newView addSuview: button]; and make sure you've added your view to another view.
Related
I'm attempting to animate a view into position in a view that's using UIDynamics. I'm not clear on the interaction between the Core Animation animations and UIDynamics but something's going on that I don't understand.
I have a test project with a view controller with a single view, a label, that starts at the bottom middle of the view and I'm trying to animate motion to the top middle.
// Create the label
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(160, 400, 100, 50)];
label.text = #"hello";
[self.view addSubview:label];
// Set up the animator and collision
self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
UICollisionBehavior *collisionBehaviour = [[UICollisionBehavior alloc] initWithItems:#[ label ]];
[collisionBehaviour addBoundaryWithIdentifier:#"wall"
fromPoint:CGPointMake(0, 50)
toPoint:CGPointMake(320, 50)];
[self.animator addBehavior:collisionBehaviour];
// Animate the label
[UIView animateWithDuration:1.0
animations:^{
label.frame = CGRectMake(160, 100, 320, 100);
}];
This is simply a line boundary at the top of the view, nowhere near either the start or end position of my animation. I don't see why it should affect the animation of the button into its initial position, but it does.
If I comment out the addBehaviour: call then the animation works fine. But when I add the behaviour, the animation goes to the wrong location. I don't understand why.
The reason why in your example the label moves left is because you are changing the width of the label from 100px to 320px and the text is right justified.
Now as for why it doesn't move up, when you add the UICollisionBehavior to the label, and then to the UIDynamicAnimator, the UIDynamicAnimator is now in charge of moving the label. When you try and animate it up, the animation goes off, then the UIDynamicAnimator sees the change and sets the center of the label back to what it originally was. You can see this for yourself if you subclass UILabel and NSLog on when the frame and center changes. If you override setCenter to just return your label moves upward.
If you want to move the label either;
Don't attach a UICollisionBehavior to the label and animate it
Also attach a UIDynamicItemBehavior to the label and then addLinearVelocity
You should not set any view's frame manually if that view is managed by a dynamic animator.
The reason why it works without calling -addBehavior: is that the dynamic animator doesn't care about the label until any of its behaviors references the label.
You can move the label manually and start the dynamic animator when the animation is finished, but the better approach is to use a UISnapBehavior to move your label to the desired position.
Also, if you change a view's frame that is managed by a dynamic animator you have to remove all behaviors associated with that view and add them again. You should set your label's initial size and only change it's position from there on, but not its size.
when you set frame of your view below that line write
[self.animator updateItemUsingCurrentState:label];
I have a view and its subview is UIScrollView and having an UIImageView which is a subview of UIScrollView. When I try to scale UIView,UIScrollView and UIImageView is also getting scaled. But the problem is I'm getting touches on half portion of view.
Here is my code
[UIView animateWithDuration:0.5 delay:0.0 options:0 animations:^{
CGFloat DesiredWidth = 250.0;
CGFloat DesiredHeight = 300.0;
CGAffineTransform scalingTransform1;
scalingTransform1 = CGAffineTransformMakeScale(DesiredWidth/self.view1.bounds.size.width,
DesiredHeight/self.view1.bounds.size.height);
self.view1.transform = scalingTransform1;
self.view1.center=CGPointMake(268, 250); }completion:^(BOOL finished)
{ }];
Please anyone can tell me how can I get touch.I searched a lot but could not find
Thanks
I think your problem is the size of the very root UIView(i.e. the superview of the UIView you talk about) of your views. When a certain subview is larger than its superview it will not receive touch events in this portions of it's frame that go outside of the superview frame.
If you have read about the responder chain in iOS you should know that UIView calls -[hitTest: withEvent:] on it's subviews in the order they have been laid out. This method checks if a certain touch points inside the UIView frame then starts to check if a certain subview can handle the touch event in the order they are positioned in the responder chain(the view hierarchy in this case).
So when the touch event is first received a certain UIView first hit tests itself and when the location of the touch is outside it's frame it does not check it's subviews. So check very carefully all your views' frames and if some root view (it could be the view property of the UIViewController you use) is actually smaller than the subview you touch.
Also mind the transforms. In the documentation is stated that when some transforms are applied the frames of the views become irrelevant.
To summarize my reply let's have this situation:
some_superview.frame = CGRectMake(0, 0, 200, 200);
some_subview_of_the_superview.frame = CGRectMake(0, 0, 200, 300);
in the region of the subview that is actually (0, 200, 200, 100) which is the bottom 100 points the touches will be ignored by the superview because they are outside it's frame
Make sure.. your subview's frame is within its superview. If it went wrong way.. it will not detect any touch.
I am probably doing something extremely stupid but I cannot figure out why this does not work.
I am trying to perform a simple UIView block animation but have run into trouble. I have recreated in a test project.
I have a view on a View controller, when I press the button, I create a new view and set its frame to be out of the current view (above it). I want to animate the transition so that the view currently on the screen moves downwards out of the view as the new one above it comes down to take its place.
Here is the code which is hooked up to the button,
the original view is hooked up as self.view1
- (IBAction)buttonPressed:(id)sender {
UIView *view2 = [[UIView alloc] init];
view2.backgroundColor = [UIColor blueColor];
float offScreenY = 0 - self.view1.frame.size.height;
CGRect offScreenRect = CGRectMake(0, offScreenY, self.view1.frame.size.width, self.view1.frame.size.height);
view2.frame = offScreenRect;
[self.view addSubview:view2];
float oldY = self.view1.frame.origin.y + self.view1.frame.size.height;
CGRect oldRect = CGRectMake(0, oldY, self.view1.frame.size.width, self.view1.frame.size.height);
[UIView animateWithDuration:0.5 animations:^{
self.view1.frame = oldRect;
view2.frame = CGRectMake(0, 0, self.view1.frame.size.width, self.view1.frame.size.height);
}];
}
This just animates view2 down and does not animate view 1.
If I do not add view 2 as a subview and only put view1's frame change in the animation block then view1 animates correctly.
BUT they will not work together!
Why is this?
This is a classic symptom of having autolayout turned on. If you animate frame, it works, but as soon autolayout reapplies the constraints on the view, view1 will return to its original location. By adding view2, iOS automatically reapplies autolayout constraints immediately and your view1 therefore won't move.
Bottom line, don't use autolayout and try to animate frame properties directly. Two solutions:
The easy solution is to turn off autolayout. Go to IB, select the "File inspector" and uncheck the "Use Autolayout" button:
If you want to keep autolayout on, you shouldn't be animating by changing the frame properties directly. You would animate by change the layout constraint constants. This has been answered elsewhere on S.O., but if you need guidance on that approach, let me know.
The basic idea, though, is to create an IBOutlet for your top constraint for view1 called, say, view1TopConstraint, and then in your animation block you can say
self.view1TopConstraint.constant += self.view1.frame.size.height;
[self.view layoutIfNeeded];
For this to work, though, you'd have to be careful about your other constraints on view1 (e.g., have a height constraint, have no bottom constraint or if you have one, lower its priority, etc.). This can be a hassle the first time you do it, but you'll quickly get the hang of animating by changing constraints.
But, then again, if you're using constraints, you probably shouldn't be defining view2 by its frame, but probably defining constraints for that, too.
In this case it is better for you to have a container view.
Add view1 and view2 inside this container accordingly. Container's some part will be in the screen and container's frame size will be double of the size of a view. Animate the container, so the other two views will be animated accordingly..
I'm making an app what contains a scroll view with some subviews what are built up on UIGestures...
The idea is to be able to use it in both orientations, in portrait.. it would be full screen but in landscape the user would have to scroll to access all the content however I have had some issues trying to get it to work correctly.
Here is how I have my view laid out:
UIView -> UIScrollView -> UIView -> UIImageView -> Subviews with Gesture recognisers.
the first UIView is just the UIViewControllers View, the UIScrollView is what will/should handle the difference in orientation, the second UIView is just a container to stop positioning screwing up and the UIImageView holds my image what is a diagram where I have placed a series of custom checkboxes and labels into/over the UIImageView to display content and receive and handle interaction...
In Portrait, this will work flawlessly however when I rotate to landscape, the content inside the UIScrollView is shifted up by 128pixels so the top is inaccessible... this also makes the bottom 128pixels too big so there is a transparent gap..
I can fix this by running this code on the didRotateFromInterfaceOrientation: function:
int offset = 0;
if (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation]))
offset = 128;
else
offset = 0;
[scrollView setContentInset:UIEdgeInsetsMake(offset, 0, -offset, 0)];
The code above just alters the ContentInset's top and bottom property to adjust the postion by 128 pixels each way... the scroll view will then display the content fine...
However, when I attempt to touch any of the gestures within 128pixels from the bottom, they will not work!
I've tried so much to get it working but also if i don't alter the contentInset they will still not work (within 128 from the bottom)... It's really confusing me as I have userInteractionEnabled set to YES on all of my views but it still wont work, anything else will and content is displayed fine within this 128pixels area...
Does anybody know what is wrong with this or have anything I can try as I feel that I have tried everything possible.
Thanks for reading,
Liam
I figured it out in the end... It was an issue with the frame size changing... all I had to do was run this code on the didRotate function:
int offset = 0;
if (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])) {
[scrollView setFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
offset = 128;
} else {
[scrollView setFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
offset = 0;
}
[scrollView setContentInset:UIEdgeInsetsMake(offset, 0, -offset, 0)];
It just makes sure that the frame is reset back to the size of the view once rotated... I figure it was an issue with autoresize masks but I didn't have the time to play around with them and this works just as well.
I've managed to manually detect rotation and set my own rotation effects between two UIViewControllers. When in the first, rotating to landscape prompts the second to be pushed on with a manual animation. The status bar moves to the correct orientation. However, the view loaded by the second UIViewController is not in the position I expected it. Instead there is margin on the left where the status bar previously was and margin/space at the bottom that I was expecting to be filled by the view.
(Click to enlarge. The orange box is simply to reference where 0,0 is in the rotated UIView, CustomView)
The code I'm using in my view controller to do the rotation is:
-(void)loadView {
CustomView *customView = [[CustomView alloc] initWithFrame:CGRectMake(0, 20, 480, 300)];
self.view = customView;
CGAffineTransform rotate = CGAffineTransformMakeRotation(degreesToRadian(90));
[self.view setTransform:rotate];
[customView release];
}
Any help would be much appreciated!
EDIT Managed to solve after a variety of trial and error approaches - answer provided below. Perhaps there's a more elegant/obvious solution though - if so feel free to provide!
try these on the viewcontrollers. Solved the 20 pixel(status bar height) clip issue when I was getting fullscreen subviews to show up.
[viewcontroller1.view setCenter:CGPointMake(viewcontroller1.view.center.x, viewcontroller1.view.center.y-20)];
and/or
[viewcontroller2.view setCenter:CGPointMake(viewcontroller2.view.center.x-20, viewcontroller2.view.center.y)];
I managed to solve this by setting the View's bounds after the transformation:
self.view.bounds = CGRectMake(20, -20, 480, 300);