I am trying to change the layout of my UIView programmatically, and not by using Auto Layout, as I am trying to deploy this app for iOS 4 and up. I basically set my view up with the screen height at 455(iPhone 5)(I know the actual screen size is 568 points, but I have a tab bar on the bottom so I am assuming the size shown in IB has taken that into account). I recorded the origin of all of my buttons and labels relative to the longer screen size and programmatically changed them to how I preferred them. The problem is, some of the buttons and labels are going off the screen now, so I think I have to perform some kind of conversion to keep them in bounds, but I am not sure what. See the pictures attached below:
Image before my initMethod is called to change button and label origins
Image after my initMethod is called to change the origin
View properties in XCode for the old screen size
This is how I want the view to Look. I moved the buttons around and recorded the origin of each.
Picture of my init method in viewWillAppear. Notice how the frame of the label is zero.
Here is the code I used:
-(void)initView
{
CGRect screenBounds = [[UIScreen mainScreen] bounds];
if (screenBounds.size.height == 480) {
//iphone 4,4s
//done
}else if(screenBounds.size.height == 568) {
//iphone 5
CGRect frame;
//Static Professor label
frame = [staticProfessorLabel frame];
frame.origin.x = 48;
frame.origin.y = 218;
[staticProfessorLabel setFrame:frame];
//Professor Label
frame = [professorLabel frame];
frame.origin.x = 192;
frame.origin.y = 218;
[professorLabel setFrame:frame];
//show button
frame = [showProfessorButton frame];
frame.origin.x = 67;
frame.origin.y = 258;
[showProfessorButton setFrame:frame];
//clear proff button
frame = [clearProfessor frame];
frame.origin.x = 240;
frame.origin.y = 258;
//note label
frame = [bottomNote frame];
frame.origin.x = 160;
frame.origin.y = 310;
[bottomNote setFrame:frame];
//search button
frame = [searchButton frame];
frame.origin.x = 158;
frame.origin.y = 424;
[searchButton setFrame:frame];
//spinner
frame = [actIndicator frame];
frame.origin.x = 266;
frame.origin.y = 424;
[actIndicator setFrame:frame];
}
}
Also, I am calling this in the viewDidAppear method because when I call it in viewDidLoad, none of the buttons and labels give me a valid frame(I am guessing they are not initialized yet).
Your Xcode screenshots show that your storyboard contains constraints, so we can tell that your storyboard has autolayout turned on. If you want to set the view frames directly in code, you have to turn off autolayout.
Take a look at my answer here if you need help turning off autolayout on your storyboard.
Your hard-coded coordinates are simply wrong. Just double-check your values.
For example, bottomNote starts at x 160, which is clearly too far to the right.
Related
I have this neat bloom effect I've created with the window frame so that the window blooms open when the application launches. I just messed with frame origin and size, is all. Now I want to do the inverse when someone clicks the red X button on the window. Someone mentioned to subclass a window and then catch an event, but I don't know how to do that yet in Objective C. Also, I need it to not hide the window yet until this anti-bloom (wither?) effect has completed.
QUESTION: How do I subclass the window and fire this witherWindow function before the window stops being visible when they click the red X button on the window's titlebar?
Here's the effect. Note that my window is a fixed size of 1000w x 680h (but had to make it 700h because of titlebar). That's why I used static numbers instead of getting the current window width/height and storing it in a property for later use, and why I didn't do division math on those numbers when playing with the origin values.
- (void)bloomWindow:(NSWindow*)window
{
NSRect frame = [window frame];
frame.origin.y -= 350.0f;
frame.origin.x -= 500.0f;
frame.size.height = 700.0f;
frame.size.width = 1000.0f;
[window setFrame:frame display:YES animate:YES];
}
- (void)witherWindow:(NSWindow*)window
{
NSRect frame = [window frame];
frame.origin.y += 350.0f;
frame.origin.x += 500.0f;
frame.size.height = 0.0f;
frame.size.width = 0.0f;
[window setFrame:frame display:YES animate:YES];
}
It's much easier than subclassing the window.
In a default Cocoa application with the AppDelegate.m class, you'll have this event:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// add stuff here
}
Now, slightly following this step, we replace // add stuff here with this:
NSButton *closeButton = [self.window standardWindowButton:NSWindowCloseButton];
[closeButton setTarget:self.window.delegate];
[closeButton setAction:#selector(applicationWillClose)];
Then, underneath applicationDidFinishLaunching, add this:
- (void)applicationWillClose;
{
[self witherWindow:self.window];
[self.window close];
}
And ensure you have that witherWindow class method as posted in the question.
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.
Alright so i was having a little bit of a problem with the view when the device went into landscape so i decided to change the location of my textfields by code with this:
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
UIInterfaceOrientation orientation = [[UIApplication sharedApplication]statusBarOrientation];
NSLog(#"SPIN");
if (orientation == UIDeviceOrientationPortrait || orientation == UIDeviceOrientationPortraitUpsideDown) {
CGRect frame = [self.a frame];
frame.origin.x = 20;
frame.origin.y = 57;
[self.a setFrame:frame];
CGRect frame2 = [self.b frame];
frame2.origin.x = 20;
frame2.origin.y = 132;
[self.b setFrame:frame2];
CGRect frame3 = [self.c frame];
frame3.origin.x = 20;
frame3.origin.y = 207;
[self.c setFrame:frame3];
} else {
CGRect frame = [self.a frame];
frame.origin.x = 83;
frame.origin.y = 21;
[self.a setFrame:frame];
CGRect frame2 = [self.b frame];
frame2.origin.x = 204;
frame2.origin.y = 21;
[self.b setFrame:frame2];
CGRect frame3 = [self.c frame];
frame3.origin.x = 330;
frame3.origin.y = 21;
[self.c setFrame:frame3];
}
}
it worked just fine, everything went where it was suppost to, the problem is when click on any of the textfields to write something all of them bug out and end up looking like this:
uh.. nevermind that apparently i need at least 10 reputation before i can post an image, so here's a description of what the image showed instead:
image shows 3 uisegmented controls and 3 textfields crammed stacked on top of each other on the very top of the screen
So i'm more of a hobbyist than a pro at this so i apologize if this is a stupid question, but what am i doing wrong here? and additionaly is there a better way of going about adapting your view?
Try using auto layout or may be auto resizing masks on different views which you are using. There is a property called as autoresizingmask in uiview.
You can check this for reference:
http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIView_Class/UIView/UIView.html
I have created a paginated UIScrollView with three subviews. It works great when testing on an iPhone 5 in landscape (the orientation I designed at) but it breaks whenever the device resolution changes.
How can I make the frame scale to the correct resolution, no matter the device or orientation?
- (void)viewDidLoad {
CGRect frame;
frame.origin.x = self.scrollView.frame.size.width * i;
frame.origin.y = 0;
frame.size = self.scrollView.frame.size;
}
- (IBAction)changePage {
CGRect frame;
frame.origin.x = self.scrollView.frame.size.width * self.pageControl.currentPage;
frame.origin.y = 0;
frame.size = self.scrollView.frame.size;
[self.scrollView scrollRectToVisible:frame animated:YES];
pageControlBeingUsed = YES;
}
Put your scroll view inside another, custom view. In the custom view, implement layoutSubviews something like this.
#interface ViewScalesOneSubview : UIView
#property UIView *scalingSubview;//subview to scale
#end
#implementation ViewScalesOneSubview
-(void) layoutSubviews {
[super layoutSubviews];
CGRect parentBounds = [self bounds];
CGRect childBounds = [scalingSubview bounds];//unscaled
CGFloat scale = parentBounds.width / childBounds.width;
CGAffineTransform transform = CGAffineTransformMakeScale( scale , scale );
//fiddle with x,y translation to position as you like
scalingSubview.transform = transform;
}
#end
Give the custom view autoresizing so it fits the window or whatever container and changes with rotation. Do not give the scroll view autoresizing, as it will conflict with this custom layoutSubviews. When the custom view changes size, it will scale the scalingSubview to fit. By using the same scale for both axis, it will preserve aspect ratio. You could scale to fit instead, or use height instead of width or whatever.
Edit:
To resize the view, as opposed to scaling the view, set the autoresizing mask.
scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
https://developer.apple.com/library/ios/documentation/uikit/reference/UIView_Class/UIView/UIView.html#//apple_ref/occ/instp/UIView/autoresizingMask
You can also do this in interface builder.
Here my dilemma. I have 4 elements inside a UIScrollView.
1. Top most element is a UILabel that I give height dynamically depending upon the amount of content in it.
2. Second is a fixed height UILabel that I give position dynamically depending upon the height given to the upper UILabel
3. Third element is a UIImageView that again I have to give position dynamically depending upon the height given to the topmost UILabel
4. The fourth is a UIWebView, to which I gave both, height & position dynamically. (Height depending upon the content in it.. and position again depending on the height of topmost UILabel)
Finally, I dynamically give height to my UIScrollView to accomodate all of the above elements.
Here is the code I use in - (void)webViewDidFinishLoad:(UIWebView *)webView to accomplish all of the above.
//Adjust height of top-most UILabel
CGSize maximumLabelSize = CGSizeMake(300,9999);
CGSize expectedLabelSize = [item.label1 sizeWithFont:label1.font constrainedToSize:maximumLabelSize lineBreakMode:label1.lineBreakMode];
CGRect newFrame = label1.frame;
newFrame.size.height = 0;
newFrame.size.height = expectedLabelSize.height;
label1.frame = newFrame;
//Adjust position of second UILlabel
CGRect labelPosition = label2.frame;
labelPosition.size.height = 20;
labelPosition.origin.y = expectedLabelSize.height +14;
label2.frame = labelPosition;
//Add UIImageView and adjust it's position
UIImageView *image;
image = [[UIImageView alloc] initWithFrame:CGRectMake(0, expectedLabelSize.height +41, 320, 2)];
image.image = [UIImage imageNamed:#"image.png"];
[scrollView addSubview:image];
[image release];
//Adjust UIWebView height and position
CGRect frame = webView.frame;
frame.size.height = 0;
frame.origin.y = expectedLabelSize.height +48;
webView.frame = frame;
CGSize fittingSize = [webView sizeThatFits:CGSizeZero];
frame.size = fittingSize;
webView.frame = frame;
//Adjust Scrollview height
scrollView.contentSize = CGSizeMake(320, fittingSize.height +expectedLabelSize.height +48);
Finally, my problem is that when I first load this view, everything but the scrollview get's proper height & position. But, if I go back one view & open this view again, the scrollview has the desired height.
Any ideas what I might be doing wrong here?
My guess is that the scrollView variable isn't yet initialized when this first runs. Try setting a breakpoint somewhere in this code and checking if scrollView has a value or if it's just 0x00000000.