UIScrollView renders different when switching back and forth using UITabBar - uitableview

I've been struggling with this problem for a couple of days now and I can't seem to find any concrete solution online, so here it goes...
The scenario is simple: I want the table view to be expanded (i.e. not scrollable) in a scroll view, and therefore I need to resize and move the view(s) within the scroll view. This I have achieved quite easy by sub classing the UIScrollView and re-implemented the layoutSubviews method (see below).
Implementation of the layoutSubViews:
- (void) layoutSubviews
{
[super layoutSubviews];
//Resize the UITableView containing all the rows (i.e. it should not scroll within the tableview)
UITableView *bt = [self.subviews objectAtIndex:0];
//1 - Set the height of the table cells
//2 - Calculate the total height for the tableview (i.e.: numberOfRows*rowHeight)
bt.frame = CGRectMake(bt.frame.origin.x, bt.frame.origin.y, bt.frame.size.width, ([bt numberOfRowsInSection:0]*bt.rowHeight));
//Move down the note text view, so that it don't overlaps the table.
UITextView *note = [self.subviews objectAtIndex:1];
note.frame = CGRectMake(note.frame.origin.x, (bt.frame.origin.y + bt.frame.size.height)+15, note.frame.size.width, note.frame.size.height);
//Set the content size to the scroll view.
//Note: If the note is hidden, then we should not include it (the same goes for the padding between note and table)
[self setContentSize: CGSizeMake(self.contentSize.width, bt.frame.size.height + note.frame.size.height)];
UIEdgeInsets insets = UIEdgeInsetsMake(0.0f, 0.0f, /*padding#bottom*/50.0f, 0.0f);
[self setContentInset:insets];
[self setScrollIndicatorInsets:insets];
}
The implementation above solves my problems and renders perfectly. Try to think of it as rendering an recipe where each row in the table view is an ingredient - that is what I'm aiming for.
My app is utilizing the UITabBar and everything renders and behave fine except for the case when I scroll down a bit in the scroll view and then switch to another tab and back. The scroll view is then somehow altered and it is no longer possible to scroll to the top (depending on how much you've scrolled down before switching tab) and is also rendered somewhat strange.
Step 1: Step1.png (see URL below)
Scrolled down to be able to see the textview below expanded tableview
Step 2: step2-switched-back.png (See URL below)
Switching to Second tab and back to First, causing odd rendering behavior and scrolling behavior where it is no longer possible to reach the first row in tableview by scrolling the scroll view.
I've created an example project since I believe the code talks for itself, and I hope someone out there can see through this and point out if I've done something wrong or if there are any way to get round this.
Project & screenshots available at: http://eddiex.se/tmp/demo/
Thanks in advance!

I've now come up with an alternative solution that will give me the same rendering as I was aiming for with having a UITableView expanded inside a UIScrollView.
Solution
I removed the UIScrollView (Editor->Unembed) and set the size of the UITableView to cover whole screen (in UI editor) and I, visually in UI editor, moved the UITextFieldView in to the UITableView (as footer view).
The tiny shadow (gradient view) below the last row in the expanded table was also easy to implement within this solution since no change was needed; UITableView's delegate implemented viewForFooterInSection method, which returned a simple gradient view.

Related

BUG - TableView with strange size and not showing scroll indicators

I do not remember the first time I saw this bug. If it started with the iOS 9 framework or not.
The problem is when I have an UITableView as first child of my UIViewController in Storyboard the tableView apparently have a bigger content size and do not show the scroll indicators. When I put a transparent UIView as first child, the problem disappear.
The same issue occurs with UITableViewControllers. And there is no trick because de TableView is the main View of the ViewController.
Anyone got this bug too or know how to fix it?
I am trying to resolve this problem in order to prevent future problems just because the Storyboard is in "MacGyver mode".
Looks like the frame of the table view is bigger than the screen size. That's why no scrolling indicators appear. When you add a transparent view as the superview of your table view, you're somehow influencing the resizing of that table view. Try inspecting the frame of the table view and then play around with its layout constraints.
Update:
I've taken the PO's code and found out that (on iPhone 6 simulator) the table view has frame = (0 0; 375 667) and contentSize: {600, 0}. This is the answer why no scrolling indicators appear. The content size is "wrong" because no data source has been specified.
And then, when I specified a data source and returned 28 for row count on section 0, I got an exception. That exception said: "failed to obtain a cell from its dataSource". That was because UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath]; returned nil (that was expected BTW). The right way of instantiating table view cells is this:
- (__kindofUITableViewCell * _Nullable)dequeueReusableCellWithIdentifier:(NSString * _Nonnull)identifier
... but it's a completely different story. Hope this helps.
The problem was occurring because in some point of the project an UITabBar of UITabBarController was override and the bottom layout guide was affected. Now, I just hide the native tab bar and put the views that I need on top of it. In this case, the native Tab Bar still there, hidden but there, so the layout guides.

CSStickyHeaderFlowLayout Header Frame

EDIT: I have create a very small app that represents perfectly the problem. https://drive.google.com/file/d/0B6sI4Feh1HJUb3pGa2pBUmY4QW8/view?usp=sharing
In the sample app, we just need to scroll down then press the button on the top bar to see the problem I am having
I am using https://github.com/jamztang/CSStickyHeaderFlowLayout to have sticky headers behaviors (like default UITableView) inside my collection view.
It works pretty well when scrolling through the collection view. I have a search bar outside the collectionview that allows users to filter the data with search text, everytime the user enters a letter, I'm refreshing the collectionview's data with the found data.
The problem is let's say there is currently 4 sections inside the collection view and that it is scrolled completely at the bottom. When I input a certian letter, it filters out everything but a single item (with a single header). The content size then changes for the collection view and displays the proper data, but then the header is too low (see screenshot).
I have investigated inside the flowlayout and inside the layoutAttributesForElementsInRect and I see it actually set the frame's origin Y to 0 (like it should be), but it seems the collectionview doesn't use this value and uses the previous one (which was when the collection view was scrolled down).
Any idea what could cause the UICollectionView to not use the desired frame inside layoutAttributesForElementsInRect ?
I tried to fix this issue, but it looks like it's impossible till UICollectionViewLayout mechanism is black box for us. Custom layout correctly returns updated frame to layout engine on reloadData:
But hidden engine not even asking attributes if they are equal. In your case you can use simple workaround. Just update contentOffset after reload.:
[self.collectionView reloadData];
self.collectionView.contentOffset = CGPointZero;

Unable to scroll to bottom with different table cell heights in RATreeView Library

I am using a library (RATreeView)
I have a perfectly working table that can expand the way I want it to, but I have the requirement to have different heights on certain sections, this seems to cause the table's height to not change in some way (regarding the scrollable surface of the UIView its added to).
So when I expand sections they allow me to scroll further down, that still works, but I am unable to see all the cells; as if the UIView or table is unable to scroll all the way. The cells differentiating heights are no problem, that comes out perfectly. I also can confirm that it does not have to do with the tab bar being on top of the content either.
- (CGFloat)treeView:(RATreeView*)treeView heightForRowForItem:(id)item {
SomeObject* levelObject = item;
if (levelObject.isChild == YES) {
if ([levelObject.type isEqualToString:#"fatty"]) {
return 140;
}
return 50;
}
return 60;
}
As you can see I set the height based on the type of the object, and whether or not it is supposed to be a section header (a child or parent). So this seems to break the RATreeView and so far unfortunately I cannot solve it. Maybe someone else has.
If any more detail is required I can elaborate.
When initialising subviews onto a view, always ensure the height you are working with is that which you think it is. I made a simple mistake that I assumed that I was working with the height of the ViewController I was currently on when in fact self.view.frame.size.height was giving me the height of the entire CollectionViewController (The entire screen). So just deduct the size of the TabBarController and the height of the ViewControllers that are also on the screen and viola, you have the correct height.

UIScrollView - Moving view to front, but below scroll indicators

I am working with a custom view which inherits from UIScrollView. The problem I am having is adding a UILabel that should be displayed at the 'top', in front of the other views. It works, but unfortunately it also covers up the scroll indicators.
As you can see, the scroll indicator is being obscured. I don't want to disable scroll indicators.
I am adding the views directly to the UIScrollView:
UILabel *subsection = [[UILabel alloc] initWithFrame:CGRectMake(0, y * h,
[self totalContentWidth], [tableViewDelegate rowHeight])];
[subsection setText:subsectionName];
[subsection setTextAlignment:NSTextAlignmentCenter];
[subSectionRows setValue:subsection forKey:key];
[self addSubview:subsection];
and then bringing them to the front, which
- (void) bringSubsectionsToFront {
for (UIView* row in [self.subSectionRows allValues]) {
[self bringSubviewToFront:row];
}
}
This behavior is confusing to me. If I peek at UIView.layer.zPosition of all the UIScrollView's subviews, they are all the same.
If I adjust the bringSubsectionsToFront method to instead move the labels in front of the view that contains the grid lines, the behavior is the same.
Looking at some internal view classes whose behavior works, it looks as if they are being added to the scroll view with: [self insertSubview:cell atIndex:0];
What am I missing here?
Solution:
verec remarked that I could find the bars if I really wanted to. So I iterated over the subview list at a suitable point, and sure enough, there they were. The order of the objects was as follows:
CustomCell
...
UIImageView
UIImageView
CustomBorderView
UILabel
UILabel
Assuming this is the z ordering, no wonder my labels were always on top.
Solution becomes simple; add UILabel's after CustomCells. This solution only works because the custom table adds CustomCells at index 0, which I believe makes scroll indicators appear after the CustomCells. If this assumption holds, I can reorder the other views relative to the CustomCells, achieving the layering effect I want.
The simplest is to use and intermediate UIView, call it 'contentView' whose children are the views you want to bringToFront.
If that contentView is what is inside the scrollView, it will stay 'below' the scroll indicators whichever contentView child you bring to the front.
The alternative would be to scan into the scrollView looking for the scroll indicators (you will find them!) but that's a compatibility risk, as you never know if/when Apple may decide to remove/reshape/reposition them according to some new (flatter ...) design paradigm ...

How can I add a scroll view to my iOS application?

I am creating an application for the iPhone which involves having buttons which animate a image view, I created another view in which the buttons are held, however I would like to be able to horizontally or vertically, scroll this view of buttons. I have looked into adding scroll views and I have even followed a tutorial, But I just can't figure out how to add it to my app. Any Ideas anyone? In the tutorial I followed the guy adds the scroll view to the MainWindow.xib, now my app is created and designed in the viewcontroller.xib. So after following the tutorial when I hit build and go it loads that nib and all you see is a white background and a scroll view. (also in that tutorial he scrolled an image where as I would like to scroll a long view of buttons) I realize the newbie-ness of my question could be crazy, but I'm new to programming all together. Here's another additional question, if someone helped me get this scroll view working where would you design the contents of that scrolled view if it's length is longer than the iphone screen? Because It is my understanding that the space to design with in interface builder is the size of the iphone screen and designing interfaces longer than the iphone screen isn't do-able. (of course I know it is do-able I just don't know how to do it).
You can set the content view (scrolling area) dimensions programmatically:
[scrollView setContentSize:CGSizeMake(contentViewWidth, contentViewHeight)];
And then add you view components to the scroll view using the coordinate system of the content view. So say your scroll view was 100x100 pixels and you wanted the scroll view to be twice as wide:
[scrollView setContentSize:CGSizeMake(200.0f, 100.0f)];
You could then add a button to appear on each half of the scroll view:
// First half of content view
button1.frame.origin.x = 10.0f;
button1.frame.origin.y = 50.0f;
[scrollView addSubView:button1];
// First half of content view
button2.frame.origin.x = 110.0f; // NOTE that 110 is outside of
button2.frame.origin.y = 50.0f; // the scroll view frame
[scrollView addSubView:button2];
You could probably design each page of the scroll view as a separate sub view in the NIB and then shift the sub views origin to the correct page location either in Interface Builder or programmatically - but I have never tried this. If this works you'd then be able to layout your buttons using IB.
I would be interested to know if this works.
most views have a .hidden property.
set it to YES to hide -> myscrollview.hidden = YES;
set to NO to show -> myscrollview.hidden = NO;
you can put this hide/show inside an animation and fade out the scrollview.
look-up "simple quartz animation" - it only a few lines of code.

Resources