AutoLayout and animateWithDuration problems - objective-c

I'm updating my iPhone app for the iPhone 5's larger screen, and there seems to be a problem with animateWithDuration:. Before I turned on autolayout, the interface elements would move slightly up the screen when the UITextField became first responder, and would move down back to their original positions when the UITextField lost its first responder status. Now, they just haphazardly fly up and off the screen, and I have no idea why. I move the objects with the code below (and this worked with autolayout turned off):
[UIView animateWithDuration:.25 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
int offset = 160;
[object1 setFrame:CGRectMake(0, -offset, object1.frame.size.width, object1.frame.size.height)];
[object2 setFrame:CGRectMake(object2.frame.origin.x, object2.frame.origin.y-offset, object2.frame.size.width, object2.frame.size.height)];
[object3 setFrame:CGRectMake(object3.frame.origin.x, object3.frame.origin.y-offset, object3.frame.size.width, object3.frame.size.height)];
}
completion:^(BOOL finished){}];
Does it have something to do with trying to move the objects by a constant? Even when I set offset to 0, all the objects still fly off the screen. I've tried detecting which screen the device has and moving the objects by different amounts, but that hasn't worked either. Does anybody have any ideas?
Thanks in advance!

When using auto layout, unless these are objects that were manually added to the view with translatesAutoresizingMaskIntoConstraints = YES, autolayout will often thwart attempts to animate by changing the frame. If there is anything in place that will cause the constraints to be reapplied, the objects will move back to wherever the constraints dictate they should be, overriding your attempts to adjust their frame properties.
If animating the movement of objects like this while using autolayout, you would either (a) create an IBOutlet for their top constraint (if adjusting y; if adjusting x, too, you would use the leading constraint also) and then change that constraint's constant property and then call layoutIfNeeded from within the animation block; or (b) remove the constraints and create new ones. (I generally do the former.)
See the latter part of this answer for an example of how to create IBOutlet for constraint and then changing the constant property in animation block.

Related

Update view constraints in NSPopover OSX Objective-C

I am on Xcode 8.2, Objective C, Mac OSX, not iOS.
I have a view shown in a popover like this:
Once i select an option in the second NSPopupButton, the blue text gets replaced with another LONGER text (5 lines instead of 3) - Unfortunately the view doesn't grow to reflect the changes!
Only if i close the NSPopover and reopen it, the view is correct:
The view is completely built with autolayout and constraints. There's NO constraint limiting HEIGHT or WIDTH for any subview, just setting distances to superview. So my quesiton is, how can i update the view after a selection in the NSPopupButton. I already tried (in the viewController)
[self.view setNeedsLayout:YES];
But it seems not to work. I checked these posts but they did not help me with my issue:
Resize current view using NSLayoutConstraints
Edit:
The new text (on NSPopupButton selection) is set like this:
[self.descriptionTextField setStringValue:self.currentSequenceItem.descriptionText];
I found the solution and i'd like to share it.
Once you've set all your constraints make sure to call
[yourTextField setPreferredMaxLayoutWidth:100.0f]; // Whatever you need
Otherwise the textField will tend to scale horizontally making all your text a one liner.
If that property is set, it'll scale vertically and will update all constraints as excpected.

How to dynamically size UITableView in Objective-C?

I'm just starting with iOS development and I was trying to achieve something that doesn't seem to work so far for me...
Here's the situation:
I have a nib file in which I have placed a UITableView, and just underneath a UIToolbar. That works just fine, the scaling is fine if I try different screensizes etc... So I was happy.
But here's the problem:
If the toolbar should be visible or not is a choice that a user can make somewhere in the application. However when the users selects to not see the toolbar I just call the method setHidden on the toolbar and pass it 'YES'.
The toolbar is now gone when I try this but the UITableView is not strechted to the bottom of the screen which gives me quite an ugly result.
So here's finally the question:
How can I automatically let the view stretch to the bottom when hiding toolbar? I guess I will have to do it in code (and not just some configuration option somewhere) but as I'm coming from Android this is somewhat strange for me.
Your best option will probably be to resize the tableview frame as you show/hide the tool bar.
CGRect frame = myTableView.frame;
frame.size.height += toolbar.frame.height;
myTableView.frame = frame;
Because you're using Auto-Layout, you'd want to create a height constraint for the UITableView and link it with your view controller via an IBOutlet. Then, to modify the height of the UITableView, simply do:
_tableViewHeightConstraint.constant += toolbar.frame.height;
You can even animate this with:
[UIView animateWithDuration:0.25 animations:^{
_tableViewHeightConstraint.constant += toolbar.frame.height;
}];
Note that you might need to call [_tableView layoutIfNeeded] after changing the height constraint.

How can I flip between two UIView?

I'm using two UITextView objects. Each UITextView represents the side of a single card in a flash card application. Just like when using regular flash cards, I want the user to have the ability to flip a card. I am asking how to flip between two UIView objects because UITextView are UIView subclasses so the same idea should work.
The animation I am looking for looks like this.
The only problem with the above example is that it utilizes two UIViewController objects and UITextView is not a subclass of UIViewController so the same principle does not apply.
Any ideas on how to do the flip animation?
You can use the UIView class method +transitionFromView:toView:duration:options:completion: to accomplish this. Both your text views need to be descendants of a common superview. Use the option UIViewAnimationOptionTransitionFlipFromLeft (or ...FromRight) to get a horizontal flip.
Looks like your question has been answered already here, just tweak the transform to make it a horizontal instead of vertical flip:
- (void)horizontalFlip {
[UIView animateWithDuration:someDuration animations:^{
yourView.layer.transform = CATransform3DMakeRotation(M_PI, 0.0, 1.0, 0.0);
} completion:^(BOOL finished){
// code to be executed when flip is completed
}];
}
As explained in that linked question, you can further modify this to do half the flip by using M_PI_2, then in the completion block of the first animation swap out the UITextViews and start a new animation to finish the flip.
Don't forget to #import math.h and #import <QuartzCore/Quartz.h> at the top of your file!
you can first hide the one UITextView and Show the another UITextView. To flip it, you can hide the displayed UITextView and show the hidden UITextView. You can also use animations explained here for it to create a flip effect.

Cause UIScrollView zoomToRect: to only display a portion

I have a UIScrollView subclass that contains a UIImageView. When the user double taps on the zone I want to zoom to that CGRect (calculated from CGPoint) and "hide" the rest of the UIImageView. The final effect is similar in Mavel.app and The Walking Dead.app when you are reading a comic.
Until now I got:
-(void)presentRect:(CGRect)rect {
self.bounds = originalFrame; //1
[UIView beginAnimations:#"navigateToPos" context:nil];
[self zoomToRect:rect animated:NO];
self.bounds = rect;
[UIView commitAnimations];
}
This works, but "zoomToRect" needs the whole bounds of the UIScrollView and when I restart it (line 1), it gives an undesired effect.
I am stuck with this. I don't know if I need a new approach or need to use another property of UIScrollView.
Any help is appreciated. Thanks.
I downloaded this apps to see it. I'm almost sure they didn't even use uiscrollview. The way the pages moves make me think about that. And uiscrollview should be used only when you really need it, because it have a lot issues when you're customizing. (tiled content, zooming and others problems).
Slide to a next page only 50px aprox., the page goes to the next, in uiscrollview it didn't happen because it works diff.
As I said before, probably it's made using 4 bars (as you correct me) or it can be done with a view wrapping the uiimage and working as a mask. You only need calculate the size of the zoom square and move it to center, and resize the wrapper view to fit this new size. The substitute for the uiscrollview for pages can be a simple image gallery, but where is images you put this "comic-view".
I've solved using other properies from UIScrollView simulating the desired effect.

Cocoa moving overlapping NSViews

I have several views inside one main view. Those views are all behind one another so that only the top one is currently seen. What I would like to do is, upon some particular user input, have one of the views in the back move lower on the screen, so it can be seen below the top view. I've been trying to do it by determining if the two views are currently overlapping, lower the one in back, if they are still overlapping, lower it more, and so on until they no longer overlap and are both entirely visible. Then I'd like for the main view to resize so that both views can be seen but I think that'd just be using setBounds or setFrame, haven't gotten there yet.
For the first problem I haven't found a way to literally see if two views overlap, if that can't be done I thought maybe by drawing rects on the view bounds and checking if those overlap, but I don't get how I can do either of these. I think bounds or frame is needed, but I'm not really sure how these are different.
Basically, I want to check if two NSViews overlap, if they do, I want to lower one of them.
EDIT: This part of the problem is pretty much solved, but now a new problem arose. I have three views, one main view, and two subviews. The main view doesn't have anything, it only contains several subviews, whatever is inside the main view is what should be seen. The two subviews are stacked on top of the main view, so that the frontmost one is the visible one, the one behind it is set to hidden. When the user hits start, the view in the back becomes visible and I set a repeating timer with a selector that will check if the views overlap, like #Vince said, if they do, it offset's the one in the back down a little and checks again until they don't overlap anymore. Using this:
- (void)updateViews {
CGRect viewFrame = [view frame]; //view is the frontmost view that will not move
CGRect backroundViewFrame = [backgroundView frame]; //the view in the back
CGRect rectIntersection = CGRectIntersection (viewFrame,
backgroundFrame);
//if the intersect is NOT null, it'll offset down by 2
if (!CGRectIsNull(rectIntersection)) {
CGRect newFrame = CGRectOffset (
rectIntersection,
0, //doesn't move in x
-2 //lowers by 2 in y
);
[backgroundView setFrame:newFrame]; //lowers the view to the new offset rect
} else{
[viewsUpdater invalidate]; //stops the timer when rects no longer intersect
viewsUpdater = nil;
}
The problem is that when I lower the backgroundView, it's only visible in the space where it intersects with the front view. Even if I expand the main view lower (which is the idea, they will all start one on top of the other with the same dimensions and when the backgroundView lowers, the main view will adjust and display both). But right now it doesn't matter how large the main view is, the backgroundView is only visible in the area where it intersects with the front view. I think it's moving to where it has to, but by the time it stops it's completely invisible, so I can't tell.
Thanks for the help.
Basically, you will be able to know if two NSView objects overlaps by getting the intersection of the two frame properties (CGRectIntersection()) and then testing for the result to be null using CGRectIsNull().
Here is the doc for it.
Now, about putting one view over another, it sounds like you need to use this method from NSView : -(void)setSubviews:(NSArray *)newSubviews;, which according to the documentation, lets you reorder the subviews.
Or simply using -(void)replaceSubview:(NSView *)oldView with:(NSView *)newView;, that said, since the oldView will be released, be careful to retain a reference to it before.
If you just attempted to move a view lower, you only have to change the origin of the reciever's frame, for example :
CGRect actualFrame = [aView frame];
CGPoint framesOrigin = actualFrame.origin;
CGRect newFrame = CGRectMake(framesOrigin.x,
(framesOrigin.y)+50,
actualFrame.width,
actualFrame.height);
[aView setFrame:newFrame];
This will move aView lower, by changing the y coordinate of its frame's origin.
Finally, about the difference between bounds and frame properties, this post on SO Difference between view's frame and view's bound + iPhone, should answer your question ;)