以下是一个 UIKit 应用程序,它使用具有列表布局和可比较数据源的集合视图。
它显示一个包含 10 个空单元格的部分,然后显示一个内容视图包含文本视图的最终单元格,该文本视图固定到内容视图的布局边距指南。
文本视图的滚动设置为 false,因此
collectionView.selfSizingInvalidation = .enabledIncludingConstraints
行将成功地使文本视图的单元格随着文本的变化而自动且动画地调整大小。
import UIKit
class ViewController: UIViewController {
var collectionView: UICollectionView!
var dataSource: UICollectionViewDiffableDataSource<String, Int>!
let textView: UITextView = {
let tv = UITextView()
tv.text = "Text"
tv.isScrollEnabled = false
return tv
}()
override func viewDidLoad() {
super.viewDidLoad()
configureHierarchy()
configureDataSource()
if #available(iOS 16.0, *) {
collectionView.selfSizingInvalidation = .enabledIncludingConstraints
}
}
func configureHierarchy() {
collectionView = .init(frame: .zero, collectionViewLayout: createLayout())
view.addSubview(collectionView)
collectionView.frame = view.bounds
collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
}
func createLayout() -> UICollectionViewLayout {
let configuration = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
return UICollectionViewCompositionalLayout.list(using: configuration)
}
func configureDataSource() {
let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, Int> { _, _, _ in }
let textViewCellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, Int> { [weak self] cell, _, _ in
guard let self else { return }
cell.contentView.addSubview(textView)
textView.pin(to: cell.contentView.layoutMarginsGuide)
}
dataSource = .init(collectionView: collectionView) { collectionView, indexPath, itemIdentifier in
if indexPath.row == 10 {
collectionView.dequeueConfiguredReusableCell(using: textViewCellRegistration, for: indexPath, item: itemIdentifier)
} else {
collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: itemIdentifier)
}
}
var snapshot = NSDiffableDataSourceSnapshot<String, Int>()
snapshot.appendSections(["section"])
snapshot.appendItems(Array(0...10))
dataSource.apply(snapshot)
}
}
extension UIView {
func pin(
to object: CanBePinnedTo,
top: CGFloat = 0,
bottom: CGFloat = 0,
leading: CGFloat = 0,
trailing: CGFloat = 0
) {
self.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
self.topAnchor.constraint(equalTo: object.topAnchor, constant: top),
self.bottomAnchor.constraint(equalTo: object.bottomAnchor, constant: bottom),
self.leadingAnchor.constraint(equalTo: object.leadingAnchor, constant: leading),
self.trailingAnchor.constraint(equalTo: object.trailingAnchor, constant: trailing),
])
}
}
@MainActor
protocol CanBePinnedTo {
var topAnchor: NSLayoutYAxisAnchor { get }
var bottomAnchor: NSLayoutYAxisAnchor { get }
var leadingAnchor: NSLayoutXAxisAnchor { get }
var trailingAnchor: NSLayoutXAxisAnchor { get }
}
extension UIView: CanBePinnedTo { }
extension UILayoutGuide: CanBePinnedTo { }
点击文本视图后,以及当文本视图更改大小时,如何通过激活
view.keyboardLayoutGuide.topAnchor
约束使 UI 移动以适应键盘,如 WWDC21 视频“键盘布局指南”中所示?
我的代码不会调整 iOS 15 上的文本视图大小,仅在 iOS 16+ 上调整,因此显然该解决方案也可能允许 UI 调整以仅在 iOS 16+ 上调整文本视图框架的更改。
以下是我在装有 iOS 17.4 的 Xcode 15.3 iPhone 15 Pro 模拟器和装有 iOS 15.8 的 iPhone SE 上尝试过但不起作用的方法:
view.keyboardLayoutGuide.topAnchor.constraint(equalTo: textView.bottomAnchor).isActive = true
在文本视图单元格注册view.keyboardLayoutGuide.topAnchor.constraint(equalTo: collectionView.bottomAnchor).isActive = true
在 viewDidLoad()
func configureHierarchy() {
collectionView = UICollectionView(frame: .zero, collectionViewLayout: createLayout())
view.addSubview(collectionView)
collectionView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
collectionView.topAnchor.constraint(equalTo: view.topAnchor),
collectionView.bottomAnchor.constraint(equalTo: view.keyboardLayoutGuide.topAnchor),
collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
])
}
更具体地说,我只在模拟器上尝试了最后一种方法,它移动的 UI 似乎是应有的两倍,而且我的选项卡栏控制器的选项卡栏是黑色的,这让我灰心丧气,尽管可能有一个仅适用于 iOS 17 的正确(非解决方法)解决方案(即说
view.keyboardLayoutGuide.usesBottomSafeArea = false
)。
前两种方法并没有起作用。
将约束优先级设置为
.defaultHigh
不起作用。
信用:https://developer.apple.com/forums/thread/756691?answerId=797384022#797384022。
接受的答案:我强烈建议您使用 UICollectionViewController,它会自动为您处理键盘避免。如果还没有的话,您可以尝试一下,看看它是否适合您的 UI。
如果您确实需要使用 UIViewController + UICollectionView,如您的示例项目所示,我们确实没有使用 UIKeyboardLayoutGuide 的记录方法,我认为值得反馈报告。 通过使用您的示例项目,我发现以下配置对我有用:
func configureHierarchy() {
...
collectionView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
collectionView.topAnchor.constraint(equalTo: view.topAnchor),
collectionView.bottomAnchor.constraint(equalTo: view.keyboardLayoutGuide.topAnchor),
collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
])
}
但是,在某些极端情况下,上述配置可能无法正常工作。如果您的实际应用程序中发生这种情况,我建议您针对该情况提交反馈报告并在此处分享您的报告 ID。