I have tableview and several controls (text fields, buttons and labels) beneath it.
When keyboard shows up I can either reduce parent view height or slide it up so my controls are accessible.
That part works fine.
However I want to adjust tableview also, so I tried reducing its height (if I reduce the height of the parent view) or moving down the origin.y coordinate (if I slide parent view up).
Neither worked, i.e. tableView would not change to the new frame, it stays the same. Tableview only resizes if I do not manipulate parent view, i.e. if I adjust tableView frame alone.
Here is the methods for that:
-(void)resizeTbl:(int)pnts{
CGRect tblframe = self.myTable.frame;
//tblframe.origin.y+=pnts;
tblframe.size.height-=pnts;
self.myTable.frame =tblframe;}
-(void )keyboardWillShow:(NSNotification *)notif{
int pnts=160;
CGRect frame = self.view.frame;
//frame.origin.y-=pnts;
frame.size.height-=pnts;
self.view.frame = frame;
[self resizeTbl:pnts];}
I probably could move all the controls up one by one and then table resize would work but I think there should be an easier way. Any advice would be greatly appreciated.
Update/workaround:
Since table resize works alone just fine, I added one more observer - keyboardDidShow, and moved resize myTable from keyboardWilShow into there to take care of tableview after keyboard is up.
-(void)keyboardDidShow:(NSNotification *)notif{
[self resizeTbl:160];}
So all the views come up as they are supposed to now.
However, when focus moves from one textbox to another with the keyboard already up, tableView resizes to its original frame by itself and I cannot catch when and how that does it. And then, when keyboard goes down tableView expands beyond its original frame, because I must have
-(void)keyboardDidHide:(NSNotification *)notif{
[self resizeTbl:-160];}
However, when focus changes again tableView shrinks back to its normal frame and everything looks just fine. If I could somehow prevent those unwanted tableview resizes that mess things up.
If someone could makes sense out all of this I would be very appreciative.
Update:
I got it. Autolayout was messing me up. I turned it off like that and my logic now works, no glitches.
What is the autoResizingMask of the view set to?
Try this when you initialize the table view.
self.myTable.autoresizingMask = UIViewAutoresizingFlexibleHeight;
This will cause the table view to be resized with its parent view.
You can't change self.view.frame. You should have a container view above self.view that holds self.myTable and all other controls you want. self.myTable and other controls should have the correct autoresizingMask.
When keyboard is shown, you resize the container view and all other views will respect their autoresizingMasks.
-(void )keyboardWillShow:(NSNotification *)notif{
int pnts=160;
CGRect rect = self.view.bounds;
rect.size.height-=pnts;
self.containerView.frame = rect;
// the following should not be needed if self.myTableView.autoresizingMask
// is at least UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleTopMargin
// but you can try adding this
self.myTableView.frame = self.containerView.bounds;
}
Related
I have a UITableViewController and I put a UIView right under the navigation item and above the actual table. The problem that I have is that the view scrolls with the tableview.
How would I get it to behave exactly like the nav bar, and have the items in the tableview scroll behind it.
Rather than having the view scroll, it should remain in its position and have everything go behind it. Sorry for reiterating, but I've found thats necessary sometimes.
The view you're placing above the cell in the storyboard becomes the table view's tableHeaderView.
You can make the header view appear fixed by resetting its frame.origin to the table view's bounds.origin every time the table view lays out its subviews:
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
UIView *header = self.tableView.tableHeaderView;
CGRect frame = header.frame;
frame.origin = self.tableView.bounds.origin;
header.frame = frame;
}
Result:
Assuming you don't want the map view to move then you could set its user interaction to false.
Alternatively you could set the header of your tableView (if you only have one section) to the map view.
I have two questions related to UITableViews.
1) The first one is, what is the gap at the top of the UITableView? My app always starts with the top cell not flush with the top of the tableview (as shown in the second image), it starts about one cell lower, i.e. where that gap is in the interface builder. I can't find where that is coming from, or why.
2) I can't seem to resize the uitableview programmatically, I'm trying to reduce the height after a popup appears. I've shown an example of it not working in the second picture.
Here is (an example of) what I am trying at the moment:
- (void)viewDidLoad
{
[super viewDidLoad];
self.table_view.delegate = self;
CGRect tableBounds = self.table_view.bounds;
tableBounds.size.height -= 100;
self.table_view.bounds = tableBounds;
CGRect tableFrame = self.table_view.frame;
tableBounds.size.height -= 100;
self.table_view.frame = tableFrame;
}
Thanks!
UITableView Selected:
Simulation:
In your xib (or storyboard) put your UITableView at position (0,0). ( the same position as the navigation bar).
The first image shows that your table view has problems even in Interface Builder. It looks as if you've set the top inset incorrectly; check your edge insets.
The reason your resizing code is not working is probably that it is too early (viewDidLoad); put it in viewDidAppear: and see if that works, and if it does, try moving it back to viewWillAppear: so the user doesn't see the resizing. If it doesn't work, it might be because you're using auto layout; you can't manually alter the frame of something whose frame is dictated by auto layout. (Your resizing code is also silly; you want to set the frame, not the bounds.) But it might also be because you're using a UITableViewController in a UINavigationController; if you do that, the table view is under the navigation controller's direct control and its size is not up to you.
I have a UIScrollView which contains a UIView and a UITableView. My goal is to adjust the height of the UIScrollView to allow me to scroll the contents of the UIScrollView to a specific point.
Here is my view: It has a UIView up top and a UITableView down below.
When I scroll, I want the UIView to stop at a specific point like so:
The tableView would be able to continue scrolling, but the UIView would be locked in place until the user scrolled up and brought the UIView back to its original state.
A prime example of what I am trying to do is the AppStore.app on iOS 6. When you view the details of the app, the filter bar for Details, Reviews and Related moves to the top of the screen and stops. I hope this all made sense.
Thanks
I ended up going with a simpler approach. can't believe I didn't see this before. I created two views, one for the UITableView's tableHeaderView and one for the viewForHeaderInSection. The view I wanted to remain visible at all times is placed in the viewForHeaderInSection method and the other view is placed in the tableHeaderView property. This is a much simpler approach, I think than using a scrollview. The only issue I have run into with this approach is all my UIView animations in these two views no longer animate.
Here is my code.
[self.tableView setTableHeaderView:self.headerView];
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
return self.tableViewHeader;
}
add yourself as a UIScrollViewDelegate to the UITableView and implement the - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView so that if your views are in their starter positions they do this:
- your UITableView animates its size to the second state:
[UIView animateWithDuration:.1f animations:^{
CGRect theFrame = myView.frame;
theFrame.size.height += floatOfIncreasedHeight;
myView.frame = theFrame;
}];
- your UIView animates its vertical movement
[UIView animateWithDuration:3 delay:0 options:UIViewAnimationOptionCurveLinear animations:^(void){
view.center = CGPointMake(view.center.x , view.center.y + floatOfVerticalMovement);
}completion:^(BOOL Finished){
view.center = CGPointMake(view.center.x , view.center.y - floatOfVerticalMovement);]
Finally always in the delegate implement – scrollViewDidScrollToTop: so that you know can animate back to the initial state (using the same techniques reversed).
UPDATE:
since your views are inside a scroll view, there is a simpler way if you are ok with the table view being partly out of bounds in your starter position (i.e. instead of changing size it just scrolls into view):
make the scroll view frame size as big as your final tableview + your initial (entire) view and place it at 0,0 (so its final part will be hidden outside of the screen)
scrollview.frame = CGRectMake(0,0,tableview.frame.size.width,tableview.frame.size.height + view.frame.size.height);
you make the container scrollview contents as big as the entire table view + the entire view + the amount of the view that you want out of the way when scrolling the table view.
scrollview.contentSize = CGSizeMake(scrollview.frame.size.width, tableview.frame.size.height + view.frame.size.height + floatOfViewHeightIWantOutOfTheWay);
you place the view one after the other in the scrollview leaving all the additional empty space after the table view
view.frame = CGRectMake(0,0,view.frame.size.width, view.frame.size.height);
tableview.frame = CGRectMake(0,view.frame.size.height, tableview.frame.size.width, tableview.frame.size.height);
now it should just work because since iOS 3 nested scrolling is supported
You can easily achieve this by setting the content size of the scrollView correctly and keep the height of the UITableView smaller than your viewcontroller's height, so that it fits the bottom part of the top UIView and the UITableView...
Another scenario is to split the top View in 2 parts.
The part that will scroll away and the part that will be visible.
Then set the part that will scroll away as the entire UITableView header and the part that will remain visible as the header view for the first table section.
So then you can achieve this with a single UITableView, without having to use a UIScrollView
What you're looking for is something like what Game Center happens to do with it's header which can actually be modelled with a table header, a custom section header view, and some very clever calculations that never actually involve messing with the frame and bounds of the table.
First, the easy part: faking a sticky view. That "view that's always present when scrolling the table" implemented as a section header. By making the number of sections in the table 1, and implementing -headerViewForSection:, it's possible to seamlessly make the view scroll with the tableview all for free (API-wise that is):
- (UITableViewHeaderFooterView *)headerViewForSection:(NSInteger)section {
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0,0,320,50)];
label.text = #"Info that was always present when scrolling the UITableView";
label.textAlignment = UITextAlignmentCenter;
label.backgroundColor = [UIColor colorWithRed:0.243 green:0.250 blue:0.253 alpha:1.000];
label.textColor = UIColor.whiteColor;
return label;
}
Finally, the hard part: KVO. When the table scrolls, we have to keep the header up there sticky with regards to the top of the view's frame, which means that you can KVO contentOffset, and use the resultant change in value to approximate the frame that the view should stick to with a little MIN() magic. Assuming your header is 44 pixels tall, the code below calculates the appropriate frame value:
CGPoint offset = [contentOffsetChange CGPointValue];
[self.tableView layoutSubviews];
self.tableView.tableHeaderView.frame = CGRectMake(0,MIN(0,offset.y),CGRectGetWidth(self.scrollView.frame),44);
If the above is infeasible, SMHeadedList actually has a fairly great, and little known, example of how complicated it can be to implement a "double tableview". That implementation has the added benefit of allowing the "header" tableview to scroll with the "main" tableview.
For future visitors, I've implemented a much simpler version, albeit one that accomplishes the goal with Reactive Cocoa, and a little bit of a different outcome. Even so, I believe it may be relevant.
What if you break the UIView into the top and bottom. The bottom will be the info.
Set UITableView.tableHeaderView = topView in viewDidLoad
and the return bottomView as Section Header in delegate method to make it float:
(UITableViewHeaderFooterView *)headerViewForSection:(NSInteger)section
{
return bottomView;
}
Just using the UITableView can solve with your problem. it is not need to use another scroll view.
set your view as the header view of UITableView. Then add your present view to the header view.
complete - (void)scrollViewDidScroll:(UIScrollView *)scrollView; . Tn the function to check the contentoffset of scroll view, and set the present view's frame.
I have a UIView that has two child elements: a UIScrollView on the upper half (which contains two UILabels), and a UITableView at the bottom. This is basically a dictionary and the purpose of the scroll view is to display the word and definition, and the table view for displaying the related words. Not all words in my dictionary have a related words array associated to them, so I hide the UITableView when that array is empty.
However, I can't get the UIScrollView to fill the entire parent view when the UITableView is hidden. Here's what I've tried so far:
- (void)updateUIWithWord:(NSString *)theWord
andDefinition:(NSString *)theDefinition
andRelatedWordsArray:(NSArray *)theRelatedWordsArray {
self.navigationItem.title = theWord;
self.word.text = theWord;
self.definition.text = theDefinition;
self.relatedWordsArray = theRelatedWordsArray;
if (![relatedWordsArray count]) {
relatedWordsTableView.hidden = YES;
// set the UITableView's width and height to 0 just to be sure
// I feel this isn't needed though
CGRect relatedWordsTableViewFrame;
relatedWordsTableViewFrame.size = CGSizeMake(0, 0);
relatedWordsTableView.frame = relatedWordsTableViewFrame;
// then make the scroll view occupy the remaining height;
// that is, the self.view's actual height
CGRect scrollViewFrame;
scrollViewFrame.origin = CGPointMake(0, 0);
scrollViewFrame.size = CGSizeMake(self.view.frame.size.width, self.view.frame.size.height);
scrollView.frame = scrollViewFrame;
}
}
Simply put, this doesn't work. For any word that has no related words and a very long definition, the scroll view simply occupies the same amount of height even with the table view gone. Help?
ADD: I tried fixing the constraints in the UIScrollView to make it relative to the top of the UITableView instead of having a fixed height, but that doesn't seem possible.
You have an "if" and an "else". Only one of those is going to execute. So when the "if" part runs and relatedWordsTableView.hidden is set to YES, the table view is hidden but nothing else happens. The "else" part isn't running so nothing is happening.
Approached the problem in a different way. I made the UIScrollView occupy the whole screen and put the UITableView inside it, below my two labels, with scrollling disabled. Now I can just hide and show it.
I'm building an iPad app with views that are split horizontally and animate in from the top and bottom (think of jaws sliding closed and open to appear and disappear respectively).
My problem is the layout of the custom jaws subview is broken only when the view loads in a landscape orientation. (The jaws-view container loads at the proper size, but the subsequent subviews for the top and bottom half are too tall, and going off the screen. They are the correct width though.)
I can start in portrait and then rotate and everything is arranged correctly.
I've tried setting the frame of the new view to the bounds of the original in a bunch of places (as suggested by many answers that didn't work for me, links upon request) but either haven't found the right spot, or need something more.
Do I need to do anything special to get the size to propagate? Is there a point before which I should not do animation? (I'm trying to move the top and bottom in my new view controller's viewDidLoad.)
The solution to this required 2 parts.
The first was described in this answer:
https://stackoverflow.com/a/8574519/1143123
which describes using viewWillAppear method instead of viewDidLoad (called earlier and "incorrect" values for bounds). This solved the problem of the view being layed out properly when loading that view in landscape (and propagated to subsequent rotations).
The second part was that the view could still get messed up if I started animating it and then did a rotation in the meantime. I changed my animation class to only move the center coordinate (as opposed to sliding the frame) which would have been better in the first place, but that didn't solve it. In the end I hardcoded the following in the ViewController for the class exhibiting these issues:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
// The container view should match the size of the current view
gameView.frame = self.view.bounds;
CGFloat width = self.view.bounds.size.width;
if(roundInProgress) {
gameView.jawsTop.frame = CGRectMake(0, 0, width, self.view.bounds.size.height/2);
gameView.jawsBottom.frame = CGRectMake(0, self.view.bounds.size.height/2, width, self.view.bounds.size.height/2);
} else {
// If round not in progress, game cards should be offscreen
CGFloat height = self.view.bounds.size.height/2;
gameView.jawsTop.frame = CGRectMake(0, -height, width, height);
gameView.jawsBottom.frame = CGRectMake(0, self.view.bounds.size.height, width, height);
}
}
Without seeing your code my guess is that it has to do with the "Autoresize Subviews" property of your parent view and/or the autosizing set-up for your subviews. Try changing that property in Interface Builder to see if that fixes your issue.