Programmatically resize NSView - objective-c

I have a main view that is supposed to display several subviews. Those subviews are directly above and below one another (in the z axis) and will drop down (in the y axis) and move up using this code:
if (!CGRectIsNull(rectIntersection)) {
CGRect newFrame = CGRectOffset (rectIntersection, 0, -2);
[backgroundView setFrame:newFrame];
} else{
[viewsUpdater invalidate];
viewsUpdater = nil;
}
rectIntersection is used to tell when the view has entirely moved down and is no longer behind the front one (when they no longer overlap rectIntersection is null), it moves down by 2 pixels at a time because this is all inside a repeating timer. I want my main view, the one that contains these two other views, to resize downward so that it expands just as the view in the background is being lowered. This is the code I'm trying for that:
CGRect mainViewFrame = [mainView frame];
if (!CGRectContainsRect(mainViewFrame, backgroundFrame)) {
CGRect newMainViewFrame = CGRectMake(0,
0,
mainViewFrame.size.width,
(mainViewFrame.size.height + 2));
[mainView setFrame:newMainViewFrame];
}
The idea is to check if the mainView contains this background view. When the backgroundView is lowered, the main view no longer contains it, and it (should) expand downward by 2 pixels. This would happen until the background view stopped moving and the mainView finally contains backgroundView.
The problem is that the mainView is not resizing at all. the background view is being lowered, and I can see it until it disappears off the bottom of the mainView. mainView should have resized but it does not change in any direction. I tried using setFrame and setBounds (with and without setNeedsDisplay) but nothing worked.
I'm really just looking for a way to programmatically change the size of the main view.

I think I understood, what the problem is. I read carefuly the code.
if (!CGRectIsNull(rectIntersection)) {
// here you set the wrong frame
//CGRect newFrame = CGRectOffset (rectIntersection, 0, -2);
CGRect newFrame = CGRectOffset (backgroundView.frame, 0, -2);
[backgroundView setFrame:newFrame];
} else{
[viewsUpdater invalidate];
viewsUpdater = nil;
}
rectIntersection is actually the intersection of the two views, that overlap, and as the backgroundView is moved downward, that rect's height decreases.
That way the mainView gets resized only one time.
To add on this, here is a simple solution using block syntax, to animate your views, this code would typically take place in your custom view controller.
// eventually a control action method, pass nil for direct call
-(void)performBackgroundViewAnimation:(id)sender {
// first, double the mainView's frame height
CGFrame newFrame = CGRectMake(mainView.frame.origin.x,
mainView.frame.origin.y,
mainView.frame.size.width,
mainView.frame.size.height*2);
// then get the backgroundView's destination rect
CGFrame newBVFrame = CGRectOffset(backgroundView.frame,
0,
-(backgroundView.frame.size.height));
// run the animation
[UIView animateWithDuration:1.0
animations:^{
mainView.frame = newFrame;
backgroundView.frame = newBVFrame;
}
];
}

Related

iOS 8.0.2 : UIView frame animation not working when UINavigationController contained inside RootViewController

I've created a RootViewController / RootView that:
Handles the content layout for the app
Exposes and interface for performing application level behaviors, like presenting the "hamburger" menu or overlay views with CAKeyframe animations.
This is in accordance with good practice.
The Problem:
When the main content view presents a form, there's a utility to animate the frame of that view, when a field is selected that would otherwise be obscured by the keyboard. This has been working fine all the way up until iOS 8.0.2
On iOS 8.0.2 the frame for the form will no longer animate if you set a negative value for origin.y. Instead of going from the current origin.y to the required origin.y it jerks down by the amount it was supposed to move, then animates back to 0.
If I present the form outside of the RootVC it works correctly.
What I've tried:
Checked that RootView is not doing anything in layout subviews to prevent the animation. (In iOS8.0 it was. I removed this and problem was solved. Only to return in iOS8.0.2)
Checked the BeginFromCurrentState flags.
Instead of animating the form view, animate [UIScreen mainScreen].keyWindow. Works but causes some other side effects that I don't want.
Question:
What has changed with animation of UIViewControllers that are contained in another view in iOS8.0.2. It seems to be something very fundamental.
The code that animates frame to move input fields out the keyboard's way:
Looks something like this:
- (void)scrollToAccommodateField:(UIView *)view
{
UIView *rootView = [UIApplication sharedApplication].keyWindow.rootViewController.view;
CGPoint position = [view convertPoint:view.bounds.origin toView:rootView];
CGFloat y = position.y;
CGFloat scrollAmount = 0;
CGFloat margin = 25;
CGSize screenSize = [self screenSizeWithOrientation:[UIApplication sharedApplication].statusBarOrientation];
CGSize accessorySize = CGSizeMake(_view.width, 44);
CGFloat maxVisibleY = screenSize.height - [self keyboardSize].height - accessorySize.height - margin;
if (y > maxVisibleY)
{
scrollAmount = maxVisibleY - y;
CGFloat scrollDelta = scrollAmount - _currentScrollAmount;
_currentScrollAmount = scrollAmount;
[self scrollByAmount:scrollDelta];
}
else
{
if (_currentScrollAmount != 0)
{
_currentScrollAmount = 0;
[UIView transitionWithView:_view duration:0.30
options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseOut animations:^
{
_view.frame = [_view bounds];
} completion:nil];
}
}
}
Update:
I've since installed TPKeyboardAvoiding pod and its working very well. . leaving this open, in case its of interest to others.

UIView animation not working with addSubview

I have an iPad app that is displaying a "note" (subclassed UILabel) with text on it. In order to navigate to the next note, I'd like it to slide it off the screen to the left while having the next one slide in from the right. I can get either animation to work, but not both at the same time.
Here's the code in my controller:
- (void)slideOutLeft {
// create the new note
flSlidingNote *newNote = [[flSlidingNote alloc] init];
newNote.text = #"blah blah";
CGRect newFrame = CGRectMake(1000, 70, 637, 297); // off the right
newNote.frame = newFrame;
[self.view addSubview:newNote];
// slide off the current one
CGRect currentFrameEnd = noteLabel.frame; // noteLabel is the existing note
currentFrameEnd.origin.x = 0 - noteLabel.frame.size.width; // off the left
[UIView animateWithDuration:1.0 animations:^{
noteLabel.frame = currentFrameEnd;
} completion:nil];
}
noteLabel does not animate at all. If I comment out the addSubview:newNote part it does. I'm still relatively new at this, so it's probably just something simple.
The problem happens whether newNote is animated or not (not animated in the code snippet).
You want to put the animation for both your views and the addSubview call for your new view in one animation block, like so:
// layout the new label like the old, but 300px offscreen right
UILabel * newNote = [[UILabel alloc] initWithFrame:CGRectOffset(self.noteLabelView.frame, 300, 0)];
newNote.text = #"NEW NOTE";
[UIView animateWithDuration:2.0f
delay:0
options:UIViewAnimationCurveEaseInOut
animations:^{
// animate the old label left 300px to offscreen left
self.noteLabelView.center = CGPointMake(self.noteLabelView.center.x-300, self.noteLabelView.center.y);
// add the new label to the view hierarchy
[self.view addSubview:newNote];
// animate the new label left 300px into the old one's spot
newNote.center = CGPointMake(newNote.center.x-300, newNote.center.y);
}
completion:^(BOOL finished) {
// remove the old label from the view hierarchy
[self.noteLabelView removeFromSuperview];
// set the property to point to the new label
self.noteLabelView = newNote;
}];
In this snippet above, I'm assuming the old label is addressable via the property self.noteLabelView.
(Also have a look at https://github.com/algal/SlidingNotes , though I can't promise that will stay there long so maybe SO isn't the right format for such a link? )
Adding to what Gabriele Petronella commented, here is a good resource on KeyFrames and AnimationGroup, with a sample GitHub project linked:
http://blog.corywiles.com/using-caanimationgroup-for-view-animations
This really helped me understand animations better.
not sure but i've seen other code that loops through subviews and performs animations, you may want to try the following:
for(UIView *view in self.subviews)
{
// perform animations
}

Custom animation between UIViewControllers using transitionFromViewController:toViewController:duration

I am trying to use UIViewController's transitionFromViewController:toViewController:duration method but with a custom animation.
I have the following two view controllers added as children to a custom container UIViewController:
firstController - This is an instance of UITabBarController
secondController - This is a subclass of UIViewController
The following code works as expected:
[self transitionFromViewController:firstController
toViewController:secondController
duration:2
options:UIViewAnimationOptionTransitionFlipFromLeft
animations:^(void){}
completion:^(BOOL finished){}];
However I would like to create a custom animation where the where firstController slides to the left and is replaced by secondController sliding in from the right similar to how UINavigationControllers push and pop methods work. After changing the options to UIViewAnimationOptionTransitionNone I have tried to implement custom animations in the animations block but have had absolutely no success. firstController is immediately swapped for secondController without and animations.
I would really appreciate any help.
Thank you
This is actually really easy. For some reason I assumed that the secondController's view would be be under/ behind that of firstController's. I had only tried to animate the firstController's view. This of course is wrong. As soon as transitionFromViewController:toViewController:duration is called secondController's view is placed over firstController's view. The following code works:
CGFloat width = self.view.frame.size.width;
CGFloat height = self.view.frame.size.height;
secondController.view.frame = CGRectMake(width, 0, width, height);
[self transitionFromViewController:firstController
toViewController:secondController
duration:0.4
options:UIViewAnimationOptionTransitionNone
animations:^(void) {
firstController.view.frame = CGRectMake(0 - width, 0, width, height);
secondController.view.frame = CGRectMake(0, 0, width, height);
}
completion:^(BOOL finished){
[secondController didMoveToParentViewController:self];
}
];

setting view boundaries

I have a scrollview with an image as a subview. I would like to set the boundaries of the scrollview to be the size of the image view, so that you wouldn't be able to see any of the background.
I don't want this happening anymore.
The weird part is, that after you zoom in or out on the image, then the boundaries seem to fix themselves, and you can no longer move the image out of the way and see the background.
This is what I have going for code:
-(UIView *) viewForZoomingInScrollView:(UIScrollView *)scrollView
{
// return which subview we want to zoom
return self.imageView;
}
-(void)viewDidLoad{
[super viewDidLoad];
[self sendLogMessage:#"Second View Controller Loaded"];
//sets the initial view to scale to fit the screen
self.imageView.frame = CGRectMake(0, 0, CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds));
//sets the content size to be the size our our whole frame
self.scrollView.contentSize = self.imageView.image.size;
//setes the scrollview's delegate to itself
self.scrollView.delegate = self;
//sets the maximum zoom to 2.0, meaning that the picture can only become a maximum of twice as big
[self.scrollView setMaximumZoomScale : 2.5];
//sets the minimum zoom to 1.0 so that the scrollview can never be smaller than the image (no matter how far in/out we're zoomed)
[self.scrollView setMinimumZoomScale : 1.0];
[imageView addSubview:button];
}
I thought that this line would solve my problem
//sets the content size to be the size our our whole frame
self.scrollView.contentSize = self.imageView.image.size;
But like I said, it only works after I zoom in or out.
EDIT: When I switch
self.scrollView.contentSize = self.imageView.image.size;
to
self.scrollView.frame = self.imageView.frame;
It works like I want it to (you can't see the background), except the toolbar on the top is covered by the image.
imageView.image.size isn't necessarily the frame of the imageView itself, try setting the
scrollview.frame = imageView.frame
and then
scrollView.contentSize = imageView.image.size
Then you won't see any border. If you want the image to be the maximum size to start with,
do
imageView.frame = image.size;
[imageView setImage:image];
scrollView.frame = self.view.frame; //or desired size
[scrollView addSubView:imageView];
[scrollView setContentSize:image.size]; //or imageView.frame.size
To fix this, I ended up declaring a new CGRect , setting its origin to my scrollView's origin, setting its size with the bounds of my view, and then assigning this CGRect back to my scrollview frame
CGRect scrollFrame;
scrollFrame.origin = self.scrollView.frame.origin;
scrollFrame.size = CGSizeMake(CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds));
self.scrollView.frame = scrollFrame;

Objective-C: Issue with CGRect .frame intersect/contains

I have two UIImageViews located about center of a horizontal screen and when the user clicks a button another UIImageView, located off screen, slides in from the right. I'm am just trying to detect when the view being brought onto the screen collides with the two static views. The problem is when I run my code and check the CGRect frames they are returned from where the views start, not end, regardless of where I place the frame calls in my method or even if I place them outside the method in a separate one. I'm a bit new to Obj-C and I understand that Core Animation runs on a separate thread and I am guessing that is why I am getting the starting values. (Correct me if I am wrong here).
So, I guess the question here is how do I detect collisions when one item is static and another is animated. Here's my code (feel free to clean it up):
- (IBAction)movegirl
{
//disabling button
self.movegirlbtn.enabled = NO;
//getting graphics center points
CGPoint pos = bushidogirl.center;
CGPoint box1 = topbox.center;
CGPoint box2 = bottombox.center;
//firing up animation
[UIView beginAnimations: nil context: NULL];
//setting animation speed
[UIView setAnimationDuration:2.0 ];
//setting final position of bushidogirl, then resetting her center position
pos.x = 260;
bushidogirl.center = pos;
//running animations
[UIView commitAnimations];
//playing gong sound
[self.audioplayer play];
//enabling button
self.movegirlbtn.enabled = YES;
[self collisiondetection: bushidogirl : topbox];
}
- (void)collisiondetection:(UIImageView *)item1 : (UIImageView *)item2
{
CGRect item1frame = item1.frame;
CGRect item2frame = item2.frame;
NSLog(#"Item1 frame = %d and Item 2 frame = %d", item1frame, item2frame);
}
How about:
if (CGRectIntersectsRect(item1frame, item2frame))
{
// collision!!
}