UIRefreshControl endRefresh 在启用 Large Title 时跳转
UIRefreshControl endRefresh jumps when used with Large Title enabled
我正在尝试使用 UIRefreshControl
,但是当我调用 endRefreshing()
时它会跳转 UINavigationBar
。只有当我将 UIRefreshControl
与大标题一起使用时才会出现此问题。
看了这里报的一些类似的问题(),我试过拖动结束才刷新,但是还是出现bug。还尝试使用
self.navigationController?.navigationBar.isTranslucent = false
和 self.extendedLayoutIncludesOpaqueBars = true
但是,none在其他问题上找到的解决方案似乎解决了问题,但仍然不顺利。
现场视频:
https://www.youtube.com/watch?v=2BBRnZ444bE
应用委托
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let window = UIWindow(frame: UIScreen.main.bounds)
window.makeKeyAndVisible()
let nav = UINavigationController()
nav.title = "My Nav"
nav.navigationBar.prefersLargeTitles = true
nav.viewControllers = [ViewController()]
window.rootViewController = nav
self.window = window
return true
}
}
请注意我使用的是大标题:
let nav = UINavigationController()
nav.title = "My Nav"
nav.navigationBar.prefersLargeTitles = true
ViewController
:
import UIKit
import Foundation
final class ViewController: UICollectionViewController {
let randomHeight = Int.random(in: 100..<300)
init() {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .vertical
layout.estimatedItemSize = CGSize(width: 20, height: 20)
super.init(collectionViewLayout: layout)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "Try to refresh"
self.navigationController?.navigationBar.isTranslucent = false
self.extendedLayoutIncludesOpaqueBars = true
collectionView.backgroundColor = .white
registerCells()
setupRefreshControl()
}
private func registerCells() {
self.collectionView.register(
Cell.self,
forCellWithReuseIdentifier: "Cell"
)
}
private func setupRefreshControl() {
let refreshControl = UIRefreshControl()
refreshControl.addTarget(
self,
action: #selector(refreshControlDidFire),
for: .valueChanged
)
self.collectionView.refreshControl = refreshControl
}
@objc private func refreshControlDidFire(_ sender: Any?) {
if let sender = sender as? UIRefreshControl, sender.isRefreshing {
refresh()
}
}
override func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
if collectionView.refreshControl!.isRefreshing {
refresh()
}
}
private func refresh() {
if !collectionView.isDragging {
collectionView.refreshControl!.endRefreshing()
collectionView.perform(#selector(collectionView.reloadData), with: nil, afterDelay: 0.05)
}
}
}
extension ViewController {
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(
_ collectionView: UICollectionView,
numberOfItemsInSection section: Int
) -> Int {
return 10
}
override func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(
withReuseIdentifier: "Cell", for: indexPath
) as? Cell else {
return UICollectionViewCell()
}
cell.label.text = "Text number \(indexPath.row), with height \(randomHeight)"
cell.heightAnchorConstraint.constant = CGFloat(randomHeight)
return cell
}
}
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 0)
}
}
final class Cell: UICollectionViewCell {
private let shadowView = UIView()
private let containerView = UIView()
private let content = UIView()
let label = UILabel()
var heightAnchorConstraint: NSLayoutConstraint!
override init(frame: CGRect = .zero) {
super.init(frame: frame)
setupViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupViews() {
insertSubview(shadowView, at: 0)
addSubview(containerView)
containerView.addSubview(label)
containerView.addSubview(content)
activateConstraints()
}
private func activateConstraints() {
self.translatesAutoresizingMaskIntoConstraints = false
shadowView.translatesAutoresizingMaskIntoConstraints = false
containerView.translatesAutoresizingMaskIntoConstraints = false
label.translatesAutoresizingMaskIntoConstraints = false
content.translatesAutoresizingMaskIntoConstraints = false
shadowView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
shadowView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
shadowView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
shadowView.bottomAnchor
.constraint(equalTo: self.bottomAnchor).isActive = true
containerView.backgroundColor = .white
containerView.layer.cornerRadius = 14
containerView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
containerView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
containerView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
containerView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
let widthAnchorConstraint = containerView.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width - 20)
widthAnchorConstraint.identifier = "Width ContainerView"
widthAnchorConstraint.priority = .defaultHigh
widthAnchorConstraint.isActive = true
label.numberOfLines = 0
label.textAlignment = .center
label.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true
label.centerYAnchor.constraint(equalTo: containerView.centerYAnchor).isActive = true
label.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true
label.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true
content.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 20).isActive = true
content.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 10).isActive = true
content.bottomAnchor.constraint(lessThanOrEqualTo: containerView.bottomAnchor, constant: -10).isActive = true
heightAnchorConstraint = content.heightAnchor.constraint(greaterThanOrEqualToConstant: 220)
heightAnchorConstraint.identifier = "Height Content"
heightAnchorConstraint.priority = .defaultHigh
heightAnchorConstraint.isActive = true
content.widthAnchor.constraint(equalToConstant: 40).isActive = true
content.backgroundColor = .red
}
override func layoutSubviews() {
super.layoutSubviews()
applyShadow(width: 0.20, height: -0.064)
}
private func applyShadow(width: CGFloat, height: CGFloat) {
let shadowPath = UIBezierPath(roundedRect: shadowView.bounds, cornerRadius: 14.0)
shadowView.layer.masksToBounds = false
shadowView.layer.shadowRadius = 8.0
shadowView.layer.shadowColor = UIColor.black.cgColor
shadowView.layer.shadowOffset = CGSize(width: width, height: height)
shadowView.layer.shadowOpacity = 0.3
shadowView.layer.shadowPath = shadowPath.cgPath
}
}
所以,我尝试了您的代码,一切正常。
但我 运行 它在 iPhone 7 - 那里没有大标题。
我认为,这是 largeTitle
的问题。
您可以尝试使用此代码段:
self.navigationController?.navigationBar.prefersLargeTitles = false
self.navigationController?.navigationBar.prefersLargeTitles = true
问题与layout.estimatedItemSize = CGSize(width: 20, height: 20)
有关
当我们使用 AutoLayout
调整单元格大小时,它会产生 UIRefreshControl
和导航栏大标题的错误。因此,如果您使用 layout.estimatedItemSize
且大小等于或大于我们的预期。所以bug不会发生,故障也不会发生。
基本上,问题是当我们调用 updateData
但单元格比我们预期的要大并且 UICollectinView
的每个单元格将调整到更大的大小然后 UICollectionViewController
将故障。
我正在尝试使用 UIRefreshControl
,但是当我调用 endRefreshing()
时它会跳转 UINavigationBar
。只有当我将 UIRefreshControl
与大标题一起使用时才会出现此问题。
看了这里报的一些类似的问题(
self.navigationController?.navigationBar.isTranslucent = false
和 self.extendedLayoutIncludesOpaqueBars = true
但是,none在其他问题上找到的解决方案似乎解决了问题,但仍然不顺利。
现场视频:
https://www.youtube.com/watch?v=2BBRnZ444bE
应用委托
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let window = UIWindow(frame: UIScreen.main.bounds)
window.makeKeyAndVisible()
let nav = UINavigationController()
nav.title = "My Nav"
nav.navigationBar.prefersLargeTitles = true
nav.viewControllers = [ViewController()]
window.rootViewController = nav
self.window = window
return true
}
}
请注意我使用的是大标题:
let nav = UINavigationController()
nav.title = "My Nav"
nav.navigationBar.prefersLargeTitles = true
ViewController
:
import UIKit
import Foundation
final class ViewController: UICollectionViewController {
let randomHeight = Int.random(in: 100..<300)
init() {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .vertical
layout.estimatedItemSize = CGSize(width: 20, height: 20)
super.init(collectionViewLayout: layout)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "Try to refresh"
self.navigationController?.navigationBar.isTranslucent = false
self.extendedLayoutIncludesOpaqueBars = true
collectionView.backgroundColor = .white
registerCells()
setupRefreshControl()
}
private func registerCells() {
self.collectionView.register(
Cell.self,
forCellWithReuseIdentifier: "Cell"
)
}
private func setupRefreshControl() {
let refreshControl = UIRefreshControl()
refreshControl.addTarget(
self,
action: #selector(refreshControlDidFire),
for: .valueChanged
)
self.collectionView.refreshControl = refreshControl
}
@objc private func refreshControlDidFire(_ sender: Any?) {
if let sender = sender as? UIRefreshControl, sender.isRefreshing {
refresh()
}
}
override func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
if collectionView.refreshControl!.isRefreshing {
refresh()
}
}
private func refresh() {
if !collectionView.isDragging {
collectionView.refreshControl!.endRefreshing()
collectionView.perform(#selector(collectionView.reloadData), with: nil, afterDelay: 0.05)
}
}
}
extension ViewController {
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(
_ collectionView: UICollectionView,
numberOfItemsInSection section: Int
) -> Int {
return 10
}
override func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(
withReuseIdentifier: "Cell", for: indexPath
) as? Cell else {
return UICollectionViewCell()
}
cell.label.text = "Text number \(indexPath.row), with height \(randomHeight)"
cell.heightAnchorConstraint.constant = CGFloat(randomHeight)
return cell
}
}
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 0)
}
}
final class Cell: UICollectionViewCell {
private let shadowView = UIView()
private let containerView = UIView()
private let content = UIView()
let label = UILabel()
var heightAnchorConstraint: NSLayoutConstraint!
override init(frame: CGRect = .zero) {
super.init(frame: frame)
setupViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupViews() {
insertSubview(shadowView, at: 0)
addSubview(containerView)
containerView.addSubview(label)
containerView.addSubview(content)
activateConstraints()
}
private func activateConstraints() {
self.translatesAutoresizingMaskIntoConstraints = false
shadowView.translatesAutoresizingMaskIntoConstraints = false
containerView.translatesAutoresizingMaskIntoConstraints = false
label.translatesAutoresizingMaskIntoConstraints = false
content.translatesAutoresizingMaskIntoConstraints = false
shadowView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
shadowView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
shadowView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
shadowView.bottomAnchor
.constraint(equalTo: self.bottomAnchor).isActive = true
containerView.backgroundColor = .white
containerView.layer.cornerRadius = 14
containerView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
containerView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
containerView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
containerView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
let widthAnchorConstraint = containerView.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width - 20)
widthAnchorConstraint.identifier = "Width ContainerView"
widthAnchorConstraint.priority = .defaultHigh
widthAnchorConstraint.isActive = true
label.numberOfLines = 0
label.textAlignment = .center
label.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true
label.centerYAnchor.constraint(equalTo: containerView.centerYAnchor).isActive = true
label.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true
label.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true
content.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 20).isActive = true
content.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 10).isActive = true
content.bottomAnchor.constraint(lessThanOrEqualTo: containerView.bottomAnchor, constant: -10).isActive = true
heightAnchorConstraint = content.heightAnchor.constraint(greaterThanOrEqualToConstant: 220)
heightAnchorConstraint.identifier = "Height Content"
heightAnchorConstraint.priority = .defaultHigh
heightAnchorConstraint.isActive = true
content.widthAnchor.constraint(equalToConstant: 40).isActive = true
content.backgroundColor = .red
}
override func layoutSubviews() {
super.layoutSubviews()
applyShadow(width: 0.20, height: -0.064)
}
private func applyShadow(width: CGFloat, height: CGFloat) {
let shadowPath = UIBezierPath(roundedRect: shadowView.bounds, cornerRadius: 14.0)
shadowView.layer.masksToBounds = false
shadowView.layer.shadowRadius = 8.0
shadowView.layer.shadowColor = UIColor.black.cgColor
shadowView.layer.shadowOffset = CGSize(width: width, height: height)
shadowView.layer.shadowOpacity = 0.3
shadowView.layer.shadowPath = shadowPath.cgPath
}
}
所以,我尝试了您的代码,一切正常。
但我 运行 它在 iPhone 7 - 那里没有大标题。
我认为,这是 largeTitle
的问题。
您可以尝试使用此代码段:
self.navigationController?.navigationBar.prefersLargeTitles = false
self.navigationController?.navigationBar.prefersLargeTitles = true
问题与layout.estimatedItemSize = CGSize(width: 20, height: 20)
当我们使用 AutoLayout
调整单元格大小时,它会产生 UIRefreshControl
和导航栏大标题的错误。因此,如果您使用 layout.estimatedItemSize
且大小等于或大于我们的预期。所以bug不会发生,故障也不会发生。
基本上,问题是当我们调用 updateData
但单元格比我们预期的要大并且 UICollectinView
的每个单元格将调整到更大的大小然后 UICollectionViewController
将故障。