I had the same problem. After fruitless attempts with UIView/UIWindow.animate/transition
and animation options, I worked around the issue as follows:
- Capture the whole screen
- Add a fullscreen UIImageView with the captured image
- Switch the user interface style in the background (which doesn't animate)
- Fade out the UIImageView
Assuming you're also working with a UITableViewController within a UINavigationController, an example:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Disable all checkmarks
tableView.visibleCells.forEach { (cell) in
cell.accessoryType = .none
}
// Set checkmark
let cell = tableView.cellForRow(at: indexPath)
cell?.accessoryType = .checkmark
// Capture and add screenshot of current screen
let imageView = UIImageView()
imageView.image = UIApplication.shared.makeSnapshot()
imageView.addTo(self.navigationController!.view).snp.makeConstraints { (maker) in
maker.edges.equalToSuperview()
}
// Set new dark mode setting and fade out screenshot
UIView.animate(withDuration: 0.5, animations: {
// Important to set it within the animations closure, so that the status bar will animate
UIApplication.shared.windows.first?.overrideUserInterfaceStyle = .dark
imageView.alpha = 0
}) { (_) in
imageView.removeFromSuperview()
}
// Deselect row
tableView.deselectRow(at: indexPath, animated: true)
}
Notes:
Result:

On the device itself, it's even smoother than the system's change animation for me!