tvOS UITableView in UINavigationController causes strange fading behavior (UIView.mask) - swift

I ran into a strange situation when a UITableView is used within the context of a UINavigationController.
tvOS uses UIView.mask to apply a "fade-out gradient" at the top and bottom of a UITableView, so that cells fade into and out of existence at the top and bottom edges of the table view.
That's fine: the fading mask always stays out of the way of the selected cell.
(Here, view.backgroundColor is set to red and tableView.backgroundColor is set to blue with 50% alpha. The constraints of the tableView are set to the safe area.)
The problem comes when you put your view controller inside of a UINavigationController. When selection is near the top, the mask view no longer seems to avoid the cell, so it looks faded. Additionally, as the user scrolls down, the fading mask takes a giant jump downward, and then as the user starts to scroll back up, that fading mask doesn't seem to get out of the way:
For reference, here is the same setup but with tableView.mask = nil:
(All fading is disabled, but you can see the cells "pop" into and out of existence at the top and bottom of the tableview. You might think you could just set tableView.masksToBounds = true, but then the selected cell gets chopped off because it grows when selected)
Surely I'm missing something obvious here? Did no one at apple put a tableview inside of a nav controller?

You were so close! The clips to bounds was missing.
tableView.clipsToBounds = true
tableView.mask = nil
Cheers :)

Related

UINavigationController weird transparency behaviour when presenting

I'm experiencing a weird glitch with the transparent navigation controller. When presenting a view controller with a UIImage at the top of the navigation controller: at first presents half of its background blur with a dark section, and half with a clear one; and after a very short moment it changes for a full dark background. As shown in the gif:
The UIImage displayed is mostly white, the borders are very close to pure white. So the grey color does not make much sense (less sense makes the rapid change).
I did disable extend edges under top bar in the Storyboard for the presented view controller. So the image is not hidden behind the Navigation Controller. With this option enabled the glitch does not appear, but I don't want to hide part of the image.
Disabling the transparency, solves my problem, but I'd like to be able to keep the transparency effect.
Edit: I did notice the "grey effect" also makes the navigation controller opaque. But only for that view, when going back, is transparent again.
Xcode 11, Swift 5, iOS 13.2, iPhone XS.
Thank you very much for your help.
I finally found the problem.
I didn't want the UIScrollView to bounce on top so I used this code:
extension ProblematicViewController: UIScrollViewDelegate {
/// Prevent bounce at top
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.contentOffset.y < 0 {
scrollView.contentOffset.y = 0
}
}
}
Which meant the ViewController's Adjust Scroll View Insets were overwrite (I think). So my scroll view did lay under the UINavigationController, to prevent this from happening, I did disable Extend Edges: Under Top Bars, so the root UIView containing the UIScrollView was not covered by the UINavigationController. So the transparency effect could never happen because the view was never under the top bar. More about Adjust Scroll View Insets on this post (helped me to find the problem).
Removing those lines and enabling Adjust Scroll View Insets and Extend Edges: Under Top Bars, solves the glitch problem, but the UIViewController does not have the "prevent bounce on top" behaviour that I wanted.
This kinda explains the weird behaviour of the UINavigationController because the SO has to calculate a transparency over nothing, but is sill strange, the transparency effect is calculated when the ProblematicViewController is fully presented, and not on viewDidLoad() or another view's life cycle before is shown to the user, so the transition is smooth, even when nothing lays behind the effect.

Run method on first set of UICollectionViewCells, but no on subsequent cells

I have a UICollectionView containing cells that are showing a preview of the camera. I've added a black UIView as a "veil" on top of the cell through storyboard.
When the camera previews begin, I animate the veil UIViews of all cells from an veil.alpha = 1 to veil.alpha = 0. This smooths out the effect of the camera preview starting.
However, this means that when a cell is initialized, but not added to the view, I have to wait until it's added to the view before I can remove the veil UIView. If I attempt beforehand, the veil UIView is nil.
The result is that as I'm scrolling, I catch a glimpse of the veil being animated away for any cells that have not been initialized beforehand.
A simple solution would be to check something like:
"If this cell is being scrolled into visibility, and the camera is already running, before the cell is visible, just set the
veil.alpha = 0."
However, I'm not sure where in the cell/controller lifecycle to put this.
I've tried setting this in prepareForReuse(), layoutSubviews() in the cell, and in cellForItemAtIndexPath in containing controller. All three of those methods cause the veils of the initial visible cells to immediately jump to veil.alpha = 0 without the animation.
Thank you.

UITableview clipsToBounds

I am creating an iPad view which has a tableview as a subview. The tableview only takes a small portion of the screen and is somewhere near the middle of the screen and it contains some menu items. I want people to be about to scroll this tableview up and down however I do not like how the cells disappear against a hard edge. When I set clipsToBounds to false, I get what I want in that the hard edge is not there anymore but the top/bottom cell disappears when the tableview needs that cell for recycling. Is there a common technique to avoid this hard-edge of when the cells scroll up against the tableview's bounds? I was thinking of adding gradient alpha masks on a parent container view but it seems a bit over the top.
There are no hard and fast rule about this, but you certainly can do whatever you feel best. What I would do in a case of floating tableView is giving it a nice border using layer. It is easy to code (2~3 lines). Round the edge to make it pretty.
If you want to drop shadow, it gets a little more complicated but possible. Just draw a bezier curve path of a rectangle (where you want your shadow to appear). Assign that CALayer shadowPath. Then add it to the table.
You can also gradient an alpha to make it appear shadow like.
But I would suggest, you set clipsToBounds to YES since it looks horrible otherwise, given the fact that the table 'floats' somewhere in your view.

Making UITableViewCells bleed over

What I'm trying to achieve might be a bit of a novelty, but maybe someone has already done this or has some great ideas.
Here's the situation: I have a UITableView sitting on top of a UIImageView (which provides the background) that has a brushed metal texture. The fist row in the table is colored black. What I'd like to achieve, is the following: when the user tries to scroll up (by pulling down) from the top of the tableview and thus causes the "bounce" physics to kick in I'd like to have the space at the top of the table view be black to seamlessly blend rather than have the user see the background image.
I can't just add another black subview under the the table because then if the tables contents is just one row it'd show under the first cell as well, considering bounce scroll lets the user scroll halfway down the screen.
I've tried setting the cells background view to a black view with a rect like 0, -100, 320, 142 (where the cell itself should be 42px high) and setting clipsToBounds to NO on both the backgroundView and the cell itself, but no dice.
Any ideas?
Set UITableView.tableHeaderView to a suitable view. Headers/footers are useful in general (you can have shadows at the end like UIWebView, or a background that moves with the cells, or whatever).
Make sure you handle the zero-cells case sensibly (although the problem isn't visible if scrolling's disabled when there aren't enough cells to require it, or if there can't be zero cells).

Making part of the view transparent/overlay while the rest isn't

I'd like to show an overlay view similar to what you see when you perform a 'Search in Contacts app where the SearchBar is visible under the toolbar while the gray overlay covers up all of the content below.
For my view, I'd like to have an UITextField and button shown visible while the rest of screen is gray with the rest of the existing contents as grayed-over and no SearchBar.
Things I tried:
I can have one view that encases
both UITextField and button with the
view's alpha level set to 0.5. But
this yields grayed appearance for
everything, including the
UITextField and button, which is not
what I'm trying to achieve.
I then tried two child views within
a parent UIView, with one subview
containing the controls while the
other one is blank. Set the parent
UIView to have 0.5 alpha -> this is
not right either.
Continuing with two child views
within a parent UIView, set the
parent view to have alpha of 1.0 and
then the the blank-view to have an
alpha level of 0.5, it's still not
right.
So what's a good way to achieve this?
Option 3 is the way to do it but make sure you're adding them in the right order i.e. transparent view added as first subView to parent view then the text field. This way the text field is on top.
Also, don't forget to set the backgroundColor attribute of the parent view to [UIColor clearColor].
I would've thought #3 would be the correct way to do it. Are you sure you have the parent and the overlay views's opaque property set to NO?