iOS - resize UITableView height programmatically - iphone

I'm having a problem trying to programmatically resize the height of a UITableView hosted within a UIViewController, using iOS5 and Storyboards. The VC displays a master/detail record style with the UITableView displaying the detail records. Depending on the type of master record shown, a set of buttons may be needed at the foot of the screen. If the buttons are not needed then I want the UITableView to extend it's height to take advantage of the space available. I'm using the following code :
CGRect tableFrame = [tableListView frame];
if (blnApprovalRec == YES)
tableFrame.size.height = 127;
else
tableFrame.size.height = 170;
[tableListView setFrame:tableFrame];
This code is called whenever the master record changes, including when the screen first loads in viewDidLoad. The problem is that when the VC loads, the UITableView doesn't paint using the size specified - it just paints with the default size from IB. Once the user changes the master record so the table is reloaded then everything works fine and the size changes as required. I've tried forcing a repaint using setNeedsDisplay, setNeedsLayout and reloadData but none of these worked.

The problem is that when the VC loads, the UITableView doesn't paint using the size specified
This happens, when table view is loaded, but it's UI is not getting refreshed. Please verify if you have forcefully called in viewDidLoad or viewWillAppear.
Hopefully your this code is in seperate method:
CGRect tableFrame = [tableListView frame];
if (blnApprovalRec == YES)
tableFrame.size.height = 127;
else
tableFrame.size.height = 170;
[tableListView setFrame:tableFrame];
When the view appear initially, you may have the default selected value from master record.
You can set that value/instance in calling function in viewWillAppear.
Can you show method name and code for, how you are calling above snippets of code forcefully?

You can resize UITableView programmatically but you need to create UITableView programmatically too. Don't use Storyboard.

I had the same issue, just needed to move the code from
- (void) viewDidLoad
to
- (void)viewDidAppear:(BOOL)animated

This is tricky, its hard to resize things like this dynamically.
i would try "setNeedsLayout" And "setNeedsDisplay" for your table, and your screen to force a redraw.
Other than that, I would storyboard it to the minimum size and use code to expand it.
Generally apple doesn't like us doing this, your buttons should be drawn over the top of the view inside another view, if thats possible.
Sorry I can't be more precise but I have solved all these issues by mucking around and with hacks, and giving up on resizing things and doing re-designs. Please let me know how you go though :)

Make sure you are setting the frame after the table has loaded.
Which method do you call that code in?

Related

Using AutoLayout in Xcode disables my code from working (dynamically)?

In my project I have enabled AutoLayout in order to make the app scale properly in both iPhone 4 and iPhone 5. Everything worked fine with doing so, but I found a new problem which I am not sure how to handle.
In my project I have a normal method which checks if a boolean is yes or no, if yes the interface should add a button into the interface and keep my tableView in its current state. But if the method returns no, the button should disappear and make the tableView's height higher! Everything with the button works fine but for some reason after start using AutoLayout my code for increasing the tableView's height stopped working (which worked before).
Now what can I do to make my tableView's height increase in height even if I am using AutoLayout? Here is my code:
- (void)viewWillAppear:(BOOL)animated
{
NSString *bookName = [self getCurrentBookName];
if([self isBlank:bookName])
{
[self.currentBookLabel setText:NSLocalizedString(#"LabelNoBookChosen", nil)];
}
else
{
[self.currentBookLabel setText:[self getCurrentBookName]];
}
[super viewWillAppear:animated];
if([self isAppLicensed] != YES) <------------------THIS IS WHERE I CHECK THE BOOLEAN METHOD!
{
[actionAppStore setHidden:TRUE];
CGRect framez = [tableView frame]; <------------------ THIS CODE IS NOT WORKING ANY LONGER!
[tableView setFrame:CGRectMake(framez.origin.x, framez.origin.y, framez.size.width, framez.size.height+77)];
}
else
{
[actionAppStore setHidden:FALSE];
}
}
You can either
Turn off autolayout and use the autosizing masks. The non-autolayout autosizing masks give you the same control over having controls increase or decrease their size for the 3.5" screen vs the 4" screen. This is probably the easiest solution. And it gives you compatibility with iOS 5 devices, too.
If you want to use autolayout, then you should create an IBOutlet for the appropriate constraint, and then programmatically change that.
On that latter example, consider a layout where I have a table view and a control at the bottom for the app store (I'm inferring this from your variable names). There are two ways to hide and show that bottom control. One is to hide it (or set its alpha to zero or to removeFromSuperview), remove the unnecessary constraints, and recreate the new appropriate constraints.
That works, but it's a hassle. I now prefer to either change the height of what I want hidden to zero, or, if it's already on the edge of the screen, I'll just slide it off the edge so you can't see it anymore.
Thus I might visually hide the app store control, not by playing around with it's hidden property, but rather by changing its height to zero (or, to show it, to 77). That way, the other constraints will automatically resize the other controls. Thus I hide it with:
self.appStoreHeightConstraint.constant = 0.0;
[self.view layoutIfNeeded];
And I show with:
self.appStoreHeightConstraint.constant = 77.0;
[self.view layoutIfNeeded];
I do that with an IBOutlet called appStoreHeightConstraint which is linked to the height constraint for that bottom control.
Alternatively (and only if the item being hidden is at the bottom), I can slide the it off the bottom of the screen (this time with a IBOutlet on the bottom constraint), with:
self.appStoreBottomConstraint.constant = 77.0;
[self.view layoutIfNeeded];
and show it with
self.appStoreBottomConstraint.constant = 0.0;
[self.view layoutIfNeeded];
In the interest of full disclosure, I should mention that you have to be very careful about designing the constraints so that they simultaneously (a) minimally describe your layout; but (b) fully describe your layout. You want to avoid the horrid conflicting or unsatisfactory constraint messages.
Focusing on the vertical dimension only, that means that have the following constraints:
table view's top constraint was to the top of the superview;
table view's bottom constraint was to the top of the app store view;
table view should have no height constraint (because that's what you want to change as other stuff changes);
the app store control at the bottom had it's top constraint linked to the tableview;
the app store's bottom constraint linked to the bottom of the superview; and
the app store's height constraint fixed at 77.0.
But I find that IB always (in a good faith effort to ensure that the constraints are unambiguous) is trying to add additional constraints (e.g. the height of the tableview). So, I personally wrestle with IB to get the constraints quite right (usually I end up lowering the priority of the table view height and when I'm all done with everything else, I can go back and get rid of the table view height).
Maybe I'm making too much of the hassles in editing the constraints, but I mention it because if you don't get them all exactly right, efforts to effect UI changes by altering one constraint will not yield the results you want. Once you get the hang of it, it's pretty easy and it's a thing of beauty to see everything adjust accordingly (esp on complicated scenes). But it can be a hassle to do those first few times.
You probably need to tell the view to layout its subviews using layoutIfNeeded.
See my question I just solved and this question is also related.

UISegmentedControl Not Redrawn

I'm using a UISegmentedControl to show headers for a table view. When the orientation changes, the headers are resized depending on the labels in the table view underneath. Despite being connected up in IB, I have to move the phone around a bit to fire a orientationChanged notification and update the widths. I've even tried setting the first segment width to 0 in IB, then again in viewDidLoad, but all headers are the same width when the view loads. I've tried using performSelector:afterDelay to make sure the view is actually on screen, and also calling setNeedsLayout and setNeedsDisplay, but nothing seems to work. I simply have to jiggle the phone around.
Is this a bug anyone has experience with or am I making a daft mistake?
I'm setting the width as below:
[segSortOrder setWidth:0.0 forSegmentAtIndex:0];
The only solution I came up with for this was to use performSelector:afterDelay and using self.view.bounds.size.width with a variable amount of padding (how much is trial and error for each ViewController. Not ideal but it does the job.

iPhone: Adding a custom view to a UITableView Cell produces weird results

What I'm doing:
I have a custom ViewController called "MoreAppsViewController" - it displays/advertises several of the other apps I have created, and I use this same class/xib etc in several different apps. It controls a small view that gets displayed on options screens.
Normally I can just add this to any view with code such as:
MoreAppsViewController * moreApps = [[MoreAppsViewController alloc] initWithNibName:#"MoreAppsViewController" bundle:nil];
[self.view addSubview:moreApps.view];
-
The Problem:
I'm trying to display the same view in a UITableViewCell. This is the code:
moreApps = [[MoreAppsViewController alloc] initWithNibName:#"MoreAppsViewController" bundle:nil];
[cell.contentView addSubview:moreApps.view];
It does get displayed. However, some elements of moreApps.view appear lower than they should - they get drawn below the cell, but the rest of the elements get drawn in the cell correctly. Specifically, it's a scroll view that forms the main part of moreApps.view that gets pushed below where it should be (the scroll view and anything on it).
Any idea why this is happening or how to fix it? Any help would be much appreciated!
-
Lastly - The scroll view is just part of the .xib file, I don't do anything weird in the MoreAppsViewController either.
EDIT:
To clarify, by 'below' I mean further down the page. If I increase the cell's height, the scroll view is just pushed further down the page.
EDIT 2:
Here's what it was doing: (the bit below the cell should be appearing inside it)
Weirdly enough, placing the scroll view inside a dummy view fixes the problem (via the interface builder). Don't ask me why - feel free to explain though!
Seems to happen for any scroll view on a .xib, when placed inside a UITableViewCell of above default height.

Can't touch UITextField on UIScrollView

I know this has been talked about a lot. I think I've gone thru every question on this site, and still have not been able to get this working.
I'm new to developing but I have a good sense of what's going on with all of my code. I definitely don't have a lot of experience though, this is my first iPhone app.
I'm making a data entry field that is comprised of multiple UITextFields within a UIScrollView. I'll avoid explaining the other details for now, as it seems its a very basic problem. Without a scrollview, the textfields work perfectly. I can touch the textfield and the keyboard or picker view show up appropriately. When I add the textfields to a scrollview, the scrollview works, but then the text fields don't receive my touches.
Here's the key: When 'User Interaction' is ENABLED, the scrollview works but the textfield touches are NOT registered. When 'User Interaction' is DISABLED, the scrollview doesn't work, but the textfield touches ARE registered and the keyboard/picker pops up.
From the other posts I have seen people creating subclasses and overriding the touches in a separate implementation. I've seen people using custom content views (subviews?), I've seen some solutions that are now obsolete since the APIs have changed in newer versions of the SDK, and I am just completely stuck.
I will leave out my code for now, because maybe there is a solution that someone has without requiring my code. If someone needs to see my code, I will put it up. My app is being written in the 3.1.3 SDK.
If anyone has ANY information that could help, it would be so greatly appreciated.
Here is what worked for me in Xcode 4.3.3.
In the storyboard, select your scrollview. Select Attribute Inspector on the right side. Uncheck Delays Content Touches.
It sounds like you're using IB to do a lot of your UI layout. If you take a programmatic approach you could set-up the following view hierarchy which should work.
Your view controller object managing your scroll view and your text fields should have a UIScrollView object and a UIView object (in addition to any UITextField objects you need). In the loadView method of the view controller class, allocate and initalize the UIView object and add your text fields to it as subviews. At the end of the method, allocate and initalize the UIScrollView object then add the UIView to the UIScrollView as a subview.
As an example, if your UIScrollView object were called scrollView and your UIView object called mainView the following lines at the end of the view controller's loadView method would properly set up the scroll view with the main view with the text fields on it:
scrollView = [[UIScrollView alloc] initWithFrame: [[UIScreen mainScreen] bounds] ];
scrollView.backgroundColor = [UIColor whiteColor];
scrollView.contentSize = mainView.frame.size;
[scrollView addSubview: mainView];
self.view = scrollView;
You may need to enable / disable user interaction for both the scroll view and text fields as necessary.
If possible you might best using different touch mechanisms for each process (scrolling and textfield input).
Maybe you can check the time differential between touch start and end such that you can detect the difference between a drag (scroll) and a tap (text input).
Having said that if you propagate the touches up from the textfields to the scrollview you should be fine. One thing I use is:
[myObject addTarget:self action:#selector(itemTouchedUpInside:) forControlEvents:UIControlEventTouchUpInside];
which passes the object touched so you can identify it and act accordingly.

Using a UITableViewController with a small-sized table?

When using a UITableViewController, the initWithStyle: method automatically creates the underlying UITableView with - according to the documentation - "the correct dimensions".
My problem is that these "correct dimensions" seem 320x460 (the iPhone's screen size), but I'm pushing this TableView/Controller pair into a UINavigationController which is itself contained in a UIView, which itself is about half the height of the screen.
No frame or bounds wrangling I can come up with seems to correctly reset the table's size, and as such it's "too long", meaning there are a collection of rows that are pushed off the bottom of the screen and are not visible nor reachable by scrolling.
So my question comes down to: what is the proper way to tell a UITableViewController to resize its component UITableView to a specified rectangle?
Thanks!
Update I've tried all the techniques suggested here to no avail, but I did find one interesting thing: if I eschew the UINavigationController altogether (which I'm not yet willing to do for production, but as an experiment), and add the table view as a direct subview of the enclosing view I mentioned, the frame size given is respected. The very moment I re-introduce the UINavigationController into the mix, no matter if it is added as a subview before or after the table view, and no matter if alloc/init it before or after the table view is added as a subview, the result is the same as it was before.
I'm beginning to suspect UINavigationController isn't much of a team player...
Update 2 The suggestion to check frame size after the table view on screen was a good one: turns out that the navigation controller is in fact resizing it some time in between load and display. My solution, hacky at best, has been to cache the frame given on load and to reset it if changed at the beginning of tableView:cellForRowAtIndexPath:. Why there you ask? Because it's the one place I found that worked, that's why!
I don't consider this a solution as it's obviously improper, but for the benefit of anyone else reading, it does seem to work.
Why not just use a regular UIViewController and create the table manually?
I had the same problem and I solved it with:
-(void) loadView {
[self setView:[[[UIView alloc] initWithFrame:CGRectZero] autorelease]];
[[self view] setAutoresizesSubviews:NO];
/* Create & configure table and other views... */
[self setResultsTable:[[RadarTableViewController alloc] initWithNibName:nil bundle:nil]];
[[resultsTable view] setFrame:CGRectMake(0,45,320,200)];
}
This is done in the parent (just a plain UIViewController in my case) controller.
I had the same problem and I solved it by resizing the tableView in the viewDidAppear function of the UITableViewController. Not the ideal solution but it works.
You can set the top margin by using :
UIEdgeInsets inset = UIEdgeInsetsMake(50, 0, 0, 0);
self.tableView.contentInset = inset;
it's Not a good practice , just in case you want more space on top you can use it .
Should not use UITableViewController , Just simply Use UIViewController and Programmatically Create UITableview
I agree with Ben's answer. I've often run into the situation where I need to resize a UITableVIew due to other controls on a view.
I usually just have a regular UIViewController with a UITableView IBOutlet. Then, if I need to, I can just manipulate the UITableView object's frame to get it to the size I need.
I'm not sure why you're creating an additional view controller for your table. However, in your code, I don't see you adding the table view to its parent. You might also try reducing the bounds height until the whole thing appears on screen; once you do that, it may give you insight as to why it's not working the way you expect.
Check the autoresizingMask and contentMode properties of the UITableView. These can both affect the frame.
i fixed this problem by this code
{
UIEdgeInsets insets;
insets.left = 0;
insets.right = 0;
insets.top = 0;
insets.bottom = 60;
self.tableView.contentInset = insets;
[self.tableView setScrollIndicatorInsets:insets];
}
If you are using Interface Builder, you can simply go to "Table View Size" properties window, and change Bottom Insets for both Content and Scroller with the height of another widget.
Try:
self.edgesForExtendedLayout = UIRectEdgeNone;
//This one in case your NavigationController is not Translucent
self.extendedLayoutIncludesOpaqueBars = NO;
Hope this helps.
Set the frame in UINavigationController.