Why is UIScrollView adding UIImageView subviews to its subviews when scrolling - ios

If title isn't telling you anything, here's what I found out recently and am curious about:
In my app I have a UIScrollView with buttons in it. These buttons all have one subview each - an instance of UILabel. Sometimes there is a particular event triggered that changes the text in labels and I also needed to change the frame of both labels and buttons, so that whole text can be displayed (it won't be truncated). So I just grabbed first subview of a button and checked how much space its text needs.
This caused crashes if that event occurred during scrolling. Turns out that besides the label, each button had another subview which was instance of UIImageView. At first I thought that during scrolling, UIScrollView takes "screenshots" of its subviews and kind of puts the images on top so that animating the scrolling is somewhat less expensive in terms of performance. This logic is flawed however, because the UIImageView was a subview at index 0, so it was put below my labels.
Anyone knows why this happens? What did Apple engineers try to achieve with this weird mechanic?
Note that it might actually happen just for buttons though. Also, I checked the labels and they didn't have any subviews.

UIButtons have a UIImageView for the background image (which is nil until you call - (void)setImage:(UIImage *)image forState:(UIControlState)state or set it using Interface Builder). So I would assume that the subview at index 0 would be the background image since that would be drawn first to be below everything else.
What you could do to be sure that you are getting the UILabel that you're looking for is something like this:
- (UILabel *)getLabelForButton:(UIButton *)button
{
for (id subView in button.subviews)
{
if ([subView isKindOfClass:[UILabel class]])
{
return subView;
}
}
return nil;
}

Related

Subviews gets trimmed when out of UITableViewCell bounds

I've been looking around and can't find a solution to my problem. I have a UIImageView as subview of UITableViewCell, I add it in cellForRowAtIndexPath, the frame of subview starts on cell-1 and goes out of cell-1, getting into cell-2.
Basically, I want my image to be on the middle of two cells. Now, when tableViewfirst loads, it appears correctly, but when I scroll the cells down and go back, the image is trimmed, showing only the part that is in cell-1 bounds.
I know iOS 7 has a scrollView between contentView and cell. When I log on my cellForRowAtIndexPath:
if (cell.contentView.superview.clipsToBounds == YES || cell.contentView.clipsToBounds == YES || cell.clipsToBounds == YES) {
NSLog(#"CLIP = YES");
} else {
NSLog(#"CLIP = NO");
}
it shows me that cell, contentView and scrollView have the property clipToBounds set to NO, which is what I want.
I just don't know why subviews still get trimmed.
I ran across the same problem as you.
I realised that the problem was my cell had non-transparent backgrounds. The subviews weren't being clipped, they were simply getting covered by the background colours of previously added cells.
You can't control the order in which cells get added to the table view, so you'd have to use [UIColor clearColor] as the background colour of your cells to get the desired effect.
If that's an issue, set the zPosition of your subview to a high enough number and you'd hopefully be good to go.

UIAccessibility Tableview Scrolling Issue

I have implemented Accessibility for a custom table view.
Where I enabled the accessibility for all the subview of table cell like imageview,label,button,textfields.
I am facing 2 issues after implementing accessibility.
Because of the the accessibility frame focus the tableviewcell frame is acting so weird. It goes either left or right on voiceover swipe event.Focus making the view frame to align based on the selection.
If the number of cells are more then scrolling is not working. Till the cells are visible accessibility is working fine. Once the focus goes to invisible cells some time the tableview is scrolling according to the voiceover selections but not as expected.
I have implemented these following methods.
-(BOOL)isAccessibilityElement
{
return NO;
}
-(NSInteger)accessibilityElementCount
{
return self.subviews.count;
}
-(id)accessibilityElementAtIndex:(NSInteger)index
{
return [self.subviews objectAtIndex:index];
}
-(NSInteger)indexOfAccessibilityElement:(id)element
{
return [self.subviews indexOfObject:element];
}
-(BOOL)shouldGroupAccessibilityChildren
{
return YES;
}
I read that these methods will not work in viewcontroller. so I am implementing these in cell class.
Do you have any idea how can I solve this accessibility Issues? Please help with your valuable suggestions.
Issue #1 can occur if your subviews (labels, buttons, as you mention) have frames that extend to larger than the size of the screen.
One visual way to verify if this is the case is to change the color of the subview: you will notice on the accessibility focus shift if a particular subview extends further than your other views.
Decrease the size of your subview frames to resolve.

Getting all subviews for a UIScrollView

I need to get an array of all the subviews in a UIScrollView. Right now I'm using
NSArray *subviews = [myScrollView subviews];
but this seems to only be returning the subviews that are visible at the time the code is run. I need all the subviews in the whole extent of the UIScrollView, even those that are currently hidden (as in off screen). How would I get that?
Essentially, I'm looking for something like the contentSize property of a UIScrollView, except instead of returning just the size of the UIScrollView if it were big enough to display all of it's content, I want it to return the content itself.
EDIT: I think I've figured it out: the scroll view this isn't working for is actually a UITableView - and I think it's deque-ing the cells that are off screen on me, and that's why they aren't showing up. I'm going to do some testing to confirm.
Try with following code its working for me.
for(UIView * subView in myScrollView.subviews ) // here write Name of you ScrollView.
{
// Here You can Get all subViews of your myScrollView.
// But For Check subview is specific UIClass such like label, button, textFiled etc.. write following code (here checking for example UILabel class).
if([subView isKindOfClass:[UILabel class]]) // Check is SubView Class Is UILabel class?
{
// You can write code here for your UILabel;
}
}
tl;dr
It turns out that
NSArray *subviews = [myScrollView subviews];
will indeed return all the subviews in a UIScrollView *myScrollView, even if they are off-screen.
The Details
The problem I was actually having was that the scroll view I was trying to use this on was actually a UITableView, and when a UITableViewCell in a UITableView goes off-screen, it actually gets removed from the UITableView - so by the time I was calling subviews, the cells I was looking for were no longer in the scroll view.
My workaround was to build all of my UITableViewCells in a separate method called by my viewDidLoad, then put all of those cells into an array. Then, instead of using subviews, I just used that array. Of course, doing it this way hurts the performance a little (in cellForRowAtIndexPath you just return the cell from the array, which is slower than the dequeueReusableCellWithIdentifier method that is typically used), but it was the only way I could find to get the behavior I needed.

UICollectionView and UINavigationController not cooperating — cells occluded by nav controller on setting prompt

In my UIViewContoller's subclass, ViewWillAppear asks whether or not there's any data to present, and if there is, changes the UINavigationController's prompt accordingly. This triggers an animation as the prompt pops into view, causing the UINavigationBar to grow in size. When this happens it partially occludes the cells in the top row of the UICollectionView.
I have a vertical Auto Layout constraint of 0, seemingly pinning the UICollectionView to its nearest neighbor, which should be its superview, but the navbar still blocks the top halves of the cells. I've tried everything — telling the CollectionView to layout its subviews, reloading data, etc., but nothing seems to work. Any idea what's going wrong?
- (void)viewWillAppear:(BOOL)animated{
if(self.orderedURLSet.count == 0){
self.navigationItem.prompt = nil;
[self.collectionView setNeedsDisplay];
} else {
self.navigationItem.prompt = #"Tap photos to edit";
}
[self.collectionView reloadData];
[self.collectionView layoutSubviews];
}
Edit: What makes this even stranger is that when I rotate orientation the collectionViewCells aren't occluded, and the full cells remain visible when I rotate back to portrait orientation. Is there some way I can "trick" my app into thinking its layout has changed and it needs to reposition the cells? LayoutSubviews isn't doing the trick.
Edit: After digging a little more into the UIView documentation, it looks like setNeedsLayout and layoutIfNeeded are really the methods I should be using, and not layoutSubviews. I've tried calling both of them, on navigationController:didShowViewController:animated:, viewWillAppear, viewDidAppear, and viewDidLayoutSubviews to no avail.
Have you tried??
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
return YES;
}

How to draw connecting lines between two uitableviewcells ios

I want to show links between two cells of uiTableView.
For Ex:
To show links between cells 1 and 5, it could be shown like:
Does any one has any idea how this can be achieved. Also when table scrolls, these links should be scrolled with it.
This looks like you want to build hierarchical view. Your implementation might be rejected by Apple due to not taking HIG into account.
Also what will be in case when lower part is not seen to user? Arrow with no end and need to scroll down for the user?
You might want to do a tree like structure (anything hierarchical) instead of ugly (sorry for that) arrows.
If you want arrow between two cell then make a separate UIView class for the Tablecell, in that UIView add one UILabel for text and one UIImageView for arrow, adjust there position as per your requirement.
Now pass this UIView to cell.
Hope this will help you.
UITableViewCell is just a subclass of UIView and UITableView is a subclass of UIScrollView. The only fanciness that UITableView provides is creating/reusing the cells and laying them out in the scroll view. (That's a gross over-simplification but for this It'll do the trick.)
So if I have a UIView subclass that draws an arrow, then it's just a matter of getting the frame for the cells I want to point to. CGRect frame1 = [[self.tableView cellForRowAtIndexPath:indexPath] frame];
Some pseudocode...
topCellFrame = get top cell frame;
bottomCellFrame = get bottom cell frame;
arrow = new arrow view;
arrow set frame = frame with origin of top cell frame origin, and height of distance from topCellFrame to bottomCellFrame;
tableView add subview = arrow;
There are edge cases to think about, If the top cell or bottom cell are offscreen the cellForRowAtIndexPath: will return nil and frame will be CGRectZero.
But I'll leave that as an exercise for you.
Edit: (I haven't done this exact thing, but I have done some similar things with the frames of cells)

Resources