当嵌套的 UITextView 开始编辑时,UICollectionView 跳转/滚动
Posted
技术标签:
【中文标题】当嵌套的 UITextView 开始编辑时,UICollectionView 跳转/滚动【英文标题】:UICollectionView jumps/scrolls when a nested UITextView begins editing 【发布时间】:2020-08-03 06:37:17 【问题描述】:关于 SO 的第一个问题,请耐心等待。我创建了一个 UICollectionViewController,它有一个标题和 1 个单元格。单元格内部是一个表格视图,表格视图内部有多个静态单元格。其中之一有一个水平的 UICollectionView,其中包含具有 UITextViews 的单元格。
问题:点击 UITextView 时,集合视图会滚动/跳转
Problem Illustration
在右侧,您可以看到 y 偏移值。在第一次点击时,它变为 267 - 标题高度。连续点击它会下降到 400 - 最底部。无论我尝试做什么,都会发生这种情况。
注意:在我的整个应用程序中,我都在使用 IQKeyboardManager
我尝试了什么:
完全禁用 IQKeyboardManager 并
-
点击文本视图
将其替换为基于旧 SO 答案的自定义键盘管理方法
设置collectionView.shouldIgnoreScrollingAdjustment = true
为:
-
VC 中的所有可滚动视图
个人可滚动视图
注意:此属性源自 IQKeyboardManager 库,据我了解,它应该禁用滚动调整偏移。
尝试在 viewDidLoad() 以及此 VC 中的所有其他位置完全禁用滚动。我用过:
collectionView.isScrollEnabled = false
collectionView.alwaysBounceVertical = false
值得注意的是,我尝试在文本 viewDidBeginEditing 以及自定义键盘管理方法中禁用滚动。
我的代码:
主 UICollectionView 及其一个单元格在情节提要中创建。其他一切都是以编程方式完成的。这是决定唯一单元格大小的流布局函数:
extension CardBuilderCollectionViewController: UICollectionViewDelegateFlowLayout
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout:
UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
let height = view.frame.size.height
let width = view.frame.size.width
return CGSize(width: width * cellWidthScale, height: height * cellHeigthScale)
另外,collectionView.contentInsetAdjustmentBehavior = .never
该单元格子类中的 TableView 是这样创建的:
let tableView: UITableView =
let table = UITableView()
table.estimatedRowHeight = 300
table.rowHeight = UITableView.automaticDimension
return table
()
和:
override func awakeFromNib()
super.awakeFromNib()
dataProvider = DataProvider(delegate: delegate)
addSubview(tableView)
tableView.fillSuperview() // Anchors to 4 corners of superview
registerCells()
tableView.delegate = dataProvider
tableView.dataSource = dataProvider
表格视图中的单元格都是GeneralTableViewCell类的子类,它包含以下确定单元格高度的方法:
var cellHeightScale: CGFloat = 0.2
didSet
setContraints()
private func setContraints()
let screen = UIScreen.main.bounds.height
let heightConstraint = heightAnchor.constraint(equalToConstant: screen*cellHeightScale)
heightConstraint.priority = UILayoutPriority(999)
heightConstraint.isActive = true
表格视图中嵌套单元格(带有TextView)的高度使用与主视图中唯一一个单元格相同的方法确定。
最后使用自定义 FlowLayout 创建标题:
class StretchyHeaderLayout: UICollectionViewFlowLayout
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]?
let layoutAttributes = super.layoutAttributesForElements(in: rect)
layoutAttributes?.forEach( (attribute) in
if attribute.representedElementKind == UICollectionView.elementKindSectionHeader && attribute.indexPath.section == 0
guard let collectionView = collectionView else return
attribute.zIndex = -1
let width = collectionView.frame.width
let contentOffsetY = collectionView.contentOffset.y
print(contentOffsetY)
if contentOffsetY > 0 return
let height = attribute.frame.height - contentOffsetY
attribute.frame = CGRect(x: 0, y: contentOffsetY, width: width, height: height)
)
return layoutAttributes
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool
return true
这是我第一次使用主要是程序化方法设计复杂的布局。因此,我可能错过了一些明显的东西。但是,尽管浏览了许多旧问题,但我无法找到解决方案。任何解决方案或指导表示赞赏。
编辑:
根据要求,这里是自定义键盘方法:
在 viewDidLoad() 中
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
然后:
var scrollOffset : CGFloat = 0
var distance : CGFloat = 0
var activeTextFeild: UITextView?
var safeArea: CGRect?
@objc func keyboardWillShow(notification: NSNotification)
if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
var safeArea = self.view.frame
safeArea.size.height += collectionView.contentOffset.y
safeArea.size.height -= keyboardSize.height + (UIScreen.main.bounds.height*0.04)
self.safeArea = safeArea
private func configureScrollView()
if let activeField = activeTextFeild
if safeArea!.contains(CGPoint(x: 0, y: activeField.frame.maxY))
print("No need to Scroll")
return
else
distance = activeField.frame.maxY - safeArea!.size.height
scrollOffset = collectionView.contentOffset.y
self.collectionView.setContentOffset(CGPoint(x: 0, y: scrollOffset + distance), animated: true)
// prevent scrolling while typing
collectionView.isScrollEnabled = false
collectionView.alwaysBounceVertical = false
@objc func keyboardWillHide(notification: NSNotification)
if distance == 0
return
// return to origin scrollOffset
self.collectionView.setContentOffset(CGPoint(x: 0, y: scrollOffset), animated: true)
scrollOffset = 0
distance = 0
collectionView.isScrollEnabled = true
最后:
//MARK: - TextViewDelegate
extension CardBuilderCollectionViewController: UITextViewDelegate
func textViewDidBeginEditing(_ textView: UITextView)
self.activeTextFeild = textView
configureScrollView()
【问题讨论】:
您能向我们展示您的自定义键盘管理吗?你在那里处理滚动? 我已将这些添加到问题的底部 【参考方案1】:我可以看到的问题是,当您的 textView 聚焦在 textViewDidBeginEditing
时,您调用了 configureScrollView()
。
distance = activeField.frame.maxY - safeArea!.size.height
scrollOffset = collectionView.contentOffset.y
self.collectionView.setContentOffset(CGPoint(x: 0, y: scrollOffset + distance), animated: true)
你打电话给collectionView.setContentOffset
--> 这就是你的收藏视图跳跃的原因。
-
请检查您的
distance
计算是否正确。另外,您的safeArea
在keyboardWillShow
时被修改。
尝试禁用setCOntentOffset
?
【讨论】:
不幸的是,这不是跳转的原因。即使没有任何这些方法,它也会跳跃。据我所知,它们根本没有效果。我尝试更改上述属性无济于事。收到通知后我也直接调用了configureScrollView(),收效甚微。以上是关于当嵌套的 UITextView 开始编辑时,UICollectionView 跳转/滚动的主要内容,如果未能解决你的问题,请参考以下文章