UICollectionView stopped showing cells after upgrading minimum deployment target to iOS 14 - ios

I have screen with two large UICollectionViewCells. Using XIB as Interface Builder. UICollectionView DataSource and Delegate is connected to my UIViewController that contains it.
Installed checkmark is true. isHidden is false. Alpha 1 for cells and UICollectionView. Also I've registered cells for UICollectionView with correct identifiers.
Before I've updated my project from minimum deployment target iOS 13 - UICollectionView works fine. After upgrading to iOS 14 minimum deployment - UICollectionView doesn't show cells. If I return minimum deployment to iOS 13 without any codebase changes - works fine.
Please anyone safe my time as I've wasted whole day already with no luck! Thanks in advance!

So I found the reason of this very strange bug. My goal was to create UICollectionView with cell which contain flexible UITextField. While user is writing - cell changes its size regarding to text height in text field.
To implement it I was using these configurations for my UICollectionView:
Image with UICollectionView configurations
Here You can see that "Cell Size" width is 0, and "Estimate Size" is automatic. It is configured for flexible cell height.
To make it work I have added this code also:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
guard let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout,
flowLayout.estimatedItemSize.width == .greatestFiniteMagnitude else {
return
}
flowLayout.estimatedItemSize = CGSize(
width: view.frame.width - Size.verticalCollectionInsetsDistance * 2,
// Make the height a reasonable estimate to
// ensure the scroll bar remains smooth
height: 200
)
}
And here is a trick:
When I had upgraded my app to iOS 14 minimum deployment this code is broken. Because, for some reason, when You set "Cell Size" width to 0 and you have iOS 13 minimum deployment, then estimatedItemSize.width == .greatestFiniteMagnitude is true. But for iOS 14 estimatedItemSize.width == .greatestFiniteMagnitude is false and estimatedItemSize.width == 0 is true instead. So now I've fixed my code by adding this condition "flowLayout.estimatedItemSize.width == 0". And here is final version:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
guard let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout,
flowLayout.estimatedItemSize.width == .greatestFiniteMagnitude || flowLayout.estimatedItemSize.width == 0 else {
return
}
flowLayout.estimatedItemSize = CGSize(
width: view.frame.width - Size.verticalCollectionInsetsDistance * 2,
// Make the height a reasonable estimate to
// ensure the scroll bar remains smooth
height: 200
)
}
P.S. I still don't understand why iOS 14 estimatedItemSize.width is different then iOS 13. If You know answer please describe it in comments. Thanks in advance!

Related

Swift button frame height issue (viewDidLayoutSubviews)

I've got some square buttons that I'd like to add rounded corners to that are proportional to the button's height. In past versions of my app, I had implemented this feature without issues using viewDidLayoutSubviews(). For some reason, after pushing a new version of my app with other features I had tweaked, this section of code no longer functions as expected. Here is the code:
override func viewDidLayoutSubviews() {
for button in buttons {
button!.layer.shadowColor = UIColor.black.cgColor
button!.layer.shadowOffset = CGSize(width: 0, height: 1.0)
button!.layer.shadowOpacity = 0.4
button!.layer.shadowRadius = button!.frame.height / 40
button!.layer.cornerRadius = button!.frame.height / 10
}
Again, this block of code used to work just fine but for some reason it no longer works. What I am experiencing is much larger relative radii on smaller buttons (iPhone SE) compared to bigger buttons (iPads).
To troubleshoot, in viewDidLayoutSubviews(), I'm printing the button!.frame.height and I'm noticing that no matter what device I use the frame height is 395.5, which I believe is the correct size only on the 12.9" iPad. Therefore, the buttons look correct on the 12.9" iPad but the radii end up being too large on all of the smaller devices.
Any idea what's going on here? Why is it that they're all returning the same frame height even though they're visually very different sizes on the different devices?
I copy and pasted the above code into the viewWillAppear() method and
the problem was resolved. I then deleted the code from
viewWillAppear(), leaving me with my original code during posting of
question, and it is continuing to run as expected (working). What
could possibly be the cause of this intermittent behavior
The reason when you initialized the buttons in viewWillAppear and remove them but it still work because your button's frame did not change in the viewDidLayoutSubview method. And the viewDidLayoutSubview is invoked only controller's view is updated, rotated, or changed, which in your case it does not.
If you try to rotate your device you will see your parent view's frame changed.
For more information about view hierarchy. See this article
Try like this:-
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
for button in buttons {
button!.layer.shadowColor = UIColor.black.cgColor
button!.layer.shadowOffset = CGSize(width: 0, height: 1.0)
button!.layer.shadowOpacity = 0.4
button!.layer.shadowRadius = button!.frame.height / 40
button!.layer.cornerRadius = button!.frame.height / 10
}

iOS 11 UITableView bug

The bug can be reproduced using the repo here.
I have a strange bug affecting my project in iOS 11 in my UITableView.
The TableView in question is grouped, has expandable cells.
Many weird effects happen that are not appearing on my iOS 10 branch:
Titles superposes
weird teleport issues when content size is above the UITableView container size when the collapsing of the cells occur
Weird teleport to top of tableview at the beginning of the scrolling when content size exceeds the container's size
Cell sizing is wrong (quite often)
There is also a ticket that seems related on the Apple Developers forum here.
I tried without any success:
if #available(iOS 11.0, *) {
tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentBehavior.never
}
I am trying to find the behavior that changed in iOS 11 that could cause this issue.
Any help would be appreciated!
edit: Clipping to bounds helped (but in the end, it hides/clips the problem). I still have a few issues (2, 3 and 4).
When I try to unwrap a cell it teleports back to the top instead of going smoothly.
When I unwrap a cell and want to scroll to it to it smoothly, it teleports to top then only scrolls to it. (had to add an additional section to show).
Here is a video of the issue (using iPhone 7 Plus, iOS 11, Xcode 9 Golden Master): https://youtu.be/XfxcmmPdeoU
In iOS 11, all the estimated UITableView properties (estimatedRowHeight, estimatedSectionHeaderHeight, and estimatedSectionFooterHeight) default to UITableViewAutomaticDimension.
I see that for your cells that's fine as you're returning UITableViewAutomaticDimension in heightForRow. For your section headers and footers however you aren't utilizing the auto-sizing. I would try disabling all the auto-sizing behavior in your headers/footers by setting estimatedSectionHeaderHeight, and estimatedSectionFooterHeight to 0.
Source: iOS 11 Floating TableView Header
Try this workarround, assuming your IBOutlets and variables are not privates in StandardHeaderView.swift:
func toggleSection(section: SectionType) {
self.sectionsOpened[section] = !self.sectionsOpened[section]!
let sectionIndex = self.sections.index(of: section)!
let indexPath = IndexPath(row: 0, section: sectionIndex)
UIView.animate(withDuration: 0.25) {
self.tableView.reloadRows(at: [indexPath], with: .automatic)
if let headerView = self.tableView.headerView(forSection: sectionIndex) as? StandardHeaderView {
headerView.configWith(title: headerView.headerTitleLabel.text!, isOpen: self.sectionsOpened[section]!, selector: headerView.selector)
}
self.tableView.scrollToRow(at: IndexPath(row: 0, section: sectionIndex), at: .top, animated: true)
}
}

iOS 11 breaks row selection

I've recently tested my app in iOS 11 and for some reason I'm not able to select one of the first 12 rows in a dynamically populated table view. The didSelectRow isn't even triggered for these rows. The other rows work fine, but even when scrolling down and back up (the cells should have been re-used again by then) the first 12 rows don't work.
Even on a static table view all cells that appear on screen when switching to that view controller will not respond, neither will controls inside them, even when they are in different sections. Cells that are out of screen initially again work fine.
I'll be trying to test this in an app with boilerplate code, but is this a known bug? I couldn't find anything online about it.
I've tested this after updating the devices to iOS 11, then again from Xcode 9 beta 6 without changes to the code, and again after migrating to Swift 4. Same behaviour inside the simulator. Up to iOS 10 everything is fine, only with iOS 11 the problem occurs.
This will break my app for users in two weeks, I need to fix it, so any help or advice very much appreciated!
UPDATE: As Paulw11 suggested, there is indeed another view blocking the rows. This was notable as row 12 could only be selected in the lower part of the cell, but not in the upper part.
The cause for this issue is the following code:
extension UIViewController {
func setBackgroundImage(forTableView tableView: UITableView) {
let bgImage = UIImage(named: "Background Image.png")
let bgImageView = UIImageView(image: bgImage)
tableView.backgroundView = bgImageView
let rect = bgImageView.bounds
let effect = UIBlurEffect(style: UIBlurEffectStyle.dark)
let blurView = UIVisualEffectView(effect: effect)
let height: CGFloat
switch screenSize.height {
case 480, 568: height = 455
case 736: height = 623
default: height = 554
}
blurView.frame = CGRect(x: 0, y: 0, width: rect.width, height: height)
let container = UIView(frame: rect)
bgImageView.addSubview(blurView)
let bgOverlay = UIImage(named: "Background Overlay.png")
let bgOverlayImageView = UIImageView(image: bgOverlay)
bgOverlayImageView.alpha = 0.15
bgImageView.addSubview(bgOverlayImageView)
self.view.insertSubview(container, at: 1)
}
}
Somehow since iOS 11 this background image seems to be rendered in front of the cells. Not inserting the container view into the table view's view will solve the issue. I've tried setting the zPosition of the container's layer but it does not help. How can I move the background image behind the cells again.
It's weird that this behaviour would change from iOS 10 to 11...
UPDATE 2: Inserting the container at index -1 fixes the issue:
self.view.insertSubview(container, at: -1)
I don't get why this works, though, shouldn't this index be out of range?
UPDATE 3: As Paulw11 pointed out below, the container is completely useless, it was left over from testing and removing it fixes the issue.
The container view seems to be appearing in front of the other views and preventing touches from making it through to the table view.
As an aside, I would see if you can refactor this to use constraints; It always worries me when you see hard-coded screen sizes, as that may break when new devices are released.

How to debug layout with Multiline UILabel / autolayout in notification content extension

How to debug the following issue? Is there a way how to work around this issue?
There seems to be a bug in iOS 10.2 and below when laying out a multi-line UILabel.
I have a fairly simple UIView subclass which I use in both app and notification content extension, that looks like this:
In the main app, everything is laid out just fine:
When shown in notification content extension on iOS 10.2 and below, the layout is broken. But only when the text is long enough to be broken into multiple lines. Seems like iOS can't calculate correct height of the whole view:
However, this issue seems to be fixed on iOS 10.3 and newer:
I started experimenting with the subviews, specifically by setting fixed height constraints.
Turns out, it was not the label(s) that caused the issue with calculating overall height but the aspect ratio constraint (width:height) on the topmost view.
Programmatically calculating height based on the view's width and setting a height constraint for the affected view helped to fix the issue:
public override func updateConstraints() {
super.updateConstraints()
if #available(iOS 10.2, *) {
imageContainerHeightConstraint.isActive = false
} else {
// FIX: multiline label / aspect ratio / autolayout bug in iOS < 10.2
let ratio: CGFloat = imageContainerAspectRatioConstraint.multiplier
imageContainerHeightConstraint.constant = round(bounds.width/ratio)
imageContainerHeightConstraint.isActive = true
}
}

Xcode 7 beta 5, Swift 2: UITableViewCell's subviews are not added to the contentView at runtime

Following phenomena happens when using Xcode 7 beta 5 and Swift 2:
When using a custom UICollectionViewCell that is created in the storyboard, the cell's subviews are not added to the cell's contentView. Thus the cell remains blank on runtime.
If I however create a custom cell class for the cell and then programmatically add the subviews to the contentView and set their frame the cell's content is displayed:
class Cell : UITableViewCell {
#IBOutlet weak var label: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
NSLog("subiews.count=%d", contentView.subviews.count) // prints "0"
contentView.subviews.count
contentView.addSubview(label)
label.frame = CGRect(x: 0, y: 0, width: 200, height: 21)
}
}
Again, without manually adding the label (that has been added in the storyboard!) and setting its frame, it would not be visible at runtime! In the storyboard the label is a subview of the content view. At run time it is not.
I cannot observe this behavior in latest Xcode 6 with Swift 1.2.
Can somebody confirm this silly behavior? And maybe provide an easier workaround?
Edit:
Luckily view constraints on the cell's subviews are applied after these views have been added programmatically to contentView. Thus at least manually setting their frames is not necessary.
There is a similar question here UITableView Empty with iOS 9 beta 5 update
And my answer for it https://stackoverflow.com/a/32052154/2674336
I can't say if this is a universal solution, but in the exact same scenario (tablviewcell content empty at runtime after updating to XCode 7 beta 5) this solved it for me:
I had to go through every single item inside the content view (including all constraints) and tick the checkbox "Installed" in the properties inspector. Initially only wR hR was checked.

Resources