Unable to resize a UIView - objective-c

I'm using xcode 4.5 (incase that makes any difference) along with a Storyboard and I'm trying to do something seemingly very simple without any luck. In my storyboard view I have placed a UIView component as you can see in the image here http://www.shopow.co.uk/assets/storyboard-view.png where the green section is the UIView which has a height of 100
In the viewDidLoad method I've added the following code
NSLog(#"View Height: %f", self.testView.bounds.size.height);
CGRect viewFrame = self.testView.frame;
CGSize newSize = CGSizeMake(viewFrame.size.width, 300);
viewFrame.size = newSize;
self.testView.frame = viewFrame;
NSLog(#"View Height: %f", self.testView.bounds.size.height);
From what I've read about resizing stuff I'd assume the green section to resize to be 300 high however this doesn't happen although the output in the log window shows the following
2012-10-19 13:05:28.015 TableTest[54318:c07] View Height: 460.000000
2012-10-19 13:05:28.016 TableTest[54318:c07] View Height: 300.000000
Notice the initial height being 460 rather than 100 which may mean something!
iOS simulator shows this http://www.shopow.co.uk/assets/simulator-view.png
I must be overlooking something obvious as far as I can tell this is not working as expected. Any guidance would be greatly appreciated.

I'm not sure if it will fix your problem, but you should really do your view resizing in viewWillAppear rather than viewDidLoad.
viewDidLoad literally means what it says, 'the view has loaded'. However, at this point your nib may not have set up the view as you would expect.
As a test, try logging the following in both viewDidLoad and viewWillAppear
NSLog(#"frame: %#", NSStringFromCGRect(self.view.frame));
You should see that in both methods you get different results. The viewWillAppear method is more likely to be correct as the view has been set up and is ready to be presented.
I personally don't use Interface Builder all that much as it causes more headaches than I can deal with but perhaps your auto-resizing masks or elastic constraints are set which is affecting the dimensions of your view?

Like #CaptainRedmuff said, you're calling it in the wrong method. viewDidLoad is called after the view is loaded, which means the frame has already been set. The method you're looking for is loadView, as viewWillAppear:animated and viewDidAppear:animated are being called after viewDidLoad.

OP wasn't really clear how he solved it. In case anyone else is having this problem.
If your view is showing the same layout as what you see in your storyboard despite the fact that you are programmatically resizing/changing its property in some way, this is because auto-layout is causing havoc.
Just select the view that you want to programmatically change and go to File Inspector in the Attributes pane. Deselect the checkbox next to Use Auto layout. Run again and you'll see your problem magically disappear.

Related

Trouble loading view when using size classes

I'm having this weird problem with size classes.
I noticed that any UIView which isn't installed in Any-Any size class is not part of the subviews when viewDidLoad is called.
Meaning I don't have access to it via my outlet nor does it appear in the [self.view subviews] array.
The subviews are placed fine on screen, and the first time I get to access them is in viewDidAppear.
I'm developing my app only for portrait iPhones so I set my Storyboard for compact width and regular height.
Am I doing something wrong? Do I need my storyboard to support Any-Any even though I don't really use this configuration?
AutoLayout hasn't completed until after the final viewDidLayoutSubviews call just before viewDidAppear.
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
//Do your job with views or with its constraints...
}
You need your storyboard to support Any-Any even though you don't really use this configuration.
You have to enable Size Classes in Interface Builder, to do that please refer the following link.
https://developer.apple.com/library/ios/recipes/xcode_help-IB_adaptive_sizes/chapters/EnablingAdaptiveSizeDesign.html#//apple_ref/doc/uid/TP40014436-CH1-SW1

Incorrect layout after rotation if banner ads enabled outside of viewDidLoad

I am having trouble with an iOS 8 app. My app needs to get information from a web server to determine if ads should be played or not. This can take a while, so the code in viewDidLoad doesn't wait for it and instead calls setCanDisplayBannerAds:NO. Then later, once the information comes in, setCanDisplayBannerAds:YES is called outside of viewDidLoad.
I have determined that I need to call setCanDisplayBannerAds:NO inside viewDidLoad otherwise setCanDisplayBannerAds:YES doesn't properly invoke the ads and generates lots of internal exceptions.
The issue is that once ads are enabled outside of viewdidLoad, the layout messes up. I've illustrated with two images below. The top one is "before", the bottom "after", as in after Show Ads is tapped.
Note in the "before" image that Show Ads and Just a Label both appear in their respective corners after rotation. In the "after" image, the landscape orientation is no longer correct. It maintains the portrait bounds with Just a Label and the banner ad cut off.
I have submitted a bug report to Apple and provided a sample project accessible on DropBox as ID19658866.zip. Feel free to download it and try it out.
If anyone can provide some insight, and maybe a solution where I don't have to change the logical sequence, i.e. not showing ads until after viewDidLoad is completed, that would be much appreciated.
Put these two lines in viewdidLoad:
[self setCanDisplayBannerAds:YES];
[self setCanDisplayBannerAds:NO];
The first call sets up the ads correctly, the second right away makes sure they don't show. Later you can call [self setCanDisplayBannerAds:YES]; and it will handle orientation changes without problem.
To be honest, Yimin Rong's answer will probably work, at least with the current SDK. But it feels a little strange. It doesn't really explain why it would work.
Looking at the documentation for setCanDisplayBannerAds: we see:
It's important to note that this will modify the view hierarchy of the view controller by inserting a new container view above the view controller's view. The impact is that the view controller's view property will no longer return the originally provided view, it will return the new container. To access the original view, use the originalContentView property.
So it actually replaces your UIViewController's .view property. Keep that in mind.
When a UIViewController is displayed onscreen, it is resized to fit the whole screen (or at least the container it is taking up). Because of this, the UIViewController's .view property gets an autoresizing mask of UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth, so its height and width are flexible, and it can stretch to fit the container.
When you call [self setCanDisplayBannerAds:YES]; in viewDidLoad, your UIViewController's .view property gets replaced with a new UIView, and since it is in viewDidLoad, its autoresizingMask gets set appropriately. Conversely, calling [self setCanDisplayBannerAds:NO]; in viewDidLoad effectively does nothing.
In your sample app, when you call [self setCanDisplayBannerAds:YES]; outside of viewDidLoad, we can see that the autoresizingMask does not in fact get set to stretch. This is what is causing the problem:
- (IBAction)TouchUpInside:(id)sender {
NSLog(#"Touch Up Inside");
NSLog(#"Original View: %#", self.view);
[self setCanDisplayBannerAds:YES];
NSLog(#"New View: %#", self.view);
NSLog(#"Content View: %#", self.originalContentView);
}
Output:
2015-02-05 11:32:19.654 ID19658866[12404:3600041] Touch Up Inside
2015-02-05 11:32:19.655 ID19658866[12404:3600041] Original View: <UIView: 0x7f9c42717440; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7f9c42717710>>
2015-02-05 11:32:19.666 ID19658866[12404:3600041] New View: <UIView: 0x7f9c42523fb0; frame = (0 0; 375 667); layer = <CALayer: 0x7f9c42524080>>
2015-02-05 11:32:19.666 ID19658866[12404:3600041] Content View: <UIView: 0x7f9c42717440; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7f9c42717710>>
The key point here being in the 3rd line of output, where "New View" does not have autoresize = W+H; included in the output. We have also confirmed that self.view gets replaced by a new view in between the 2nd and 3rd lines of output.
To remedy this, we can make sure our new self.view property gets the required autoresizingMask by adding this line after we call [self setCanDisplayBannerAds:YES];:
self.view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
And it will work, and we will know why.
Or we could, you know... just replace our self.view property by calling [self setCanDisplayBannerAds:YES]; within viewDidLoad, so it automatically gets the autoresizingMask set correctly.
Total shot in the dark, but what happens if you call setNeedsLayout or setneedsupdatecontraint (provided you're using auto layout, which I would assume you are) once the ads come in? Also, make sure your constraints are properly setup between the ad view and the others. These probably are not the correct answer but I'm hoping it will provide some additional insight as to the real problem/solution.
Frankly, Yimin Rong has the best solution out of all the available. However, if this is a premium v. standard account type of thing, wouldn't it be best if you were to keep a variable locally stored and have the user hit the "restore purchase" button. This way, even if the user were offline, you could display ads (if they were stored locally) and when they came online, you could collect your profits from the advertisers. Best of luck with your application!

NSPopover padding content on one side

I have two NSPopovers, both of which are set up exactly the same (just linking a custom NSView from IB). Both pop up just fine, but one appears to offset the content by about 20px.
This NSPopover is (properly) not padding the content...
but this one adds about 20px from the ride side.
Here are the two views laid out in IB
As you can see, the search bar should be pinned to the right side like it is the left, but for some reason it is not. At first I thought it was a contraints issue, but after messing around with them for a while I can confirm it is not.
Any clue whats up?
EDIT: Decided to subclass the view and fill its rect, got some very strange results! The view appears to be offset. I have no clue why this is...
From here, this caught my eye (emphasis mine):
The principle circumstance in which you should not call
setTranslatesAutoresizingMaskIntoConstraints: is when you are not the
person who specifies a view’s relation to its container. For example,
an NSTableRowView instance is placed by NSTableView. It might do this
by allowing the autoresizing mask to be translated into constraints,
or it might not. This is a private implementation detail. Other views
on which you should not call
setTranslatesAutoresizingMaskIntoConstraints: include an
NSTableCellView, a subview of NSSplitView, an NSTabViewItem’s view, or
the content view of an NSPopover, NSWindow, or NSBox. For those
familiar with classic Cocoa layout: If you would not call
setAutoresizingMask: on a view in classic style, you should not call
setTranslatesAutoresizingMaskIntoConstraints: under autolayout.
Does it apply to you?
if you have an outlet to your content view, you should be able to force it into place with:
[contentView setFrame:[[contentView superview]bounds]];
or more modernly I guess...
contentView.frame = contentView.superview.bounds;
Proplem solved, but I still don't exactly know why it required solving. The NSPopovers content view (or at least mine) requires an origin point of X: 13, Y: 13. For some reason, only one of my views was getting this value.
To solve this, I subclassed NSView and overrode the setFrame method forcing its x and y to always be 13, 13
-(void)setFrame:(NSRect)frameRect {
[super setFrame:NSMakeRect(13, 13, frameRect.size.width, frameRect.size.height)];
}

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.

Using a patternimage for a View inside an NSScrollView

I have a quite big problem, I am really not able to solve myself.
The result should look like this:
This image was made with photoshop and is part of the interface I try to build.
In the middle you see something, that should be a list of projects, you should be able to scroll, if it the list is bigger then the view.
So I am making a scrollview like this: (for some reason I cannot do this in the interface builder and want this to work programmatically)
NSScrollView *projectsListView = [[NSScrollView alloc] initWithFrame:NSMakeRect(15, 2, 801, 588)];
[projectsListView setHasVerticalScroller:YES];
Then I create the content view and set a pattern image as backgroundcolor:
NSClipView *contentView = [[NSClipView alloc] initWithFrame:NSMakeRect(0, 0,
[projectsListView frame].size.width, [projectsListView frame].size.height+(98*2))];
[contentView setBackgroundColor:[NSColor colorWithPatternImage:[NSImage imageNamed:#"BoxLineBackground"]]];
[contentView setDrawsBackground:YES];
Then set the view as document view:
[projectsListView setDocumentView:contentView];
Should work, right?
However the content view gets clipped and looks like this while scrolling:
I tried this to fix it, but it does nothing:
[[projectsListView documentView] setCopiesOnScroll:NO];
I also tried this, but it causes the contentview not to scroll at all.
The image stays the same, but I can move the scroller normally.
[[projectsListView contentView] setCopiesOnScroll:NO];
If I try to set the contentview with setContentView: instead of using setDocumentView:
it may work, but the scroller is gone, so it is also not working correctly.
I would really like to use the patternimage method, because I cannot tell how long the list will be. It depends on the user.
An additional problem then would be to get the whole thing rounded, but that does not matter that much. I tried to use a transparent border image and to overlay the NSScrollView with it using an NSImageView, but again this causes corruption, because it clips and moves the overlaying parts of the image view together with the content of the scrollview.
Anyone having an idea, how to achieve this?
Thanks
Rather than re-inventing the wheel, this interface should be implemented with a view-based NSTableView. The table cell UI could then be created in Interface Builder and you could control the background of the cells using the various NSTableView delegate methods. NSTableView will handle redraws upon scrolling correctly.
To handle the pattern color, just make the background of your cell a custom subclass of NSTableCellView and implement your pattern drawing code.
Regardless of all this, the problem you are having is due to an NSScrollView drawing optimisation. You can turn this off by calling [[yourScrollView contentView] setCopiesOnScroll:NO] on your NSScrollView instance. You can also set this in Interface Builder, just un-check the Copies on Scroll checkbox.
I fixed the problem by setting the Background Color on the NSScrollView instead on the NSClipView.
I though the background would be static in that case and I need to set it for the content view for that reason, but it works pretty well and does scroll together with the content view.
And thanks for Rob Keniger's answer. I will probably try this out.