Swift实现TableView滚动时动态调整UICollectionView与TableView高度占比
Hey there! Let's tackle your problem step by step—you're dealing with screen jank when resizing your collection/table views, want a smoother progressive adjustment via pan gestures, plus a better way to handle collection view cell sizing. Here's how to fix this properly:
1. Ditch Constant Updates—Use Constraint Multipliers Instead
Your original setup uses equal-height constraints relative to the parent view, so modifying the multiplier of the collection view's height constraint is the right approach (not adjusting constants). This keeps your layout logic aligned with Auto Layout's intended behavior.
First, add an IBOutlet for your collection view's height constraint in MainScreenVC:
@IBOutlet weak var collectionViewHeightConstraint: NSLayoutConstraint!
2. Implement Pan Gesture for Smooth Progressive Resizing
Since the table view has its own scroll gesture, we need to make the pan gesture play nice with it. Here's how to set it up:
override func viewDidLoad() { super.viewDidLoad() // Add pan gesture to the table view let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:))) panGesture.cancelsTouchesInView = false // Allow table view to receive touches too panGesture.delegate = self tableView.addGestureRecognizer(panGesture) } // Allow simultaneous recognition of pan and table view scroll gestures extension MainScreenVC: UIGestureRecognizerDelegate { func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { return true } } @objc private func handlePan(_ gesture: UIPanGestureRecognizer) { let translation = gesture.translation(in: view) let viewHeight = view.bounds.height // Define your target multipliers let defaultCollectionMultiplier: CGFloat = 1/3 // 1:2 split let minimizedCollectionMultiplier: CGFloat = 1/5 // 1:4 split let multiplierRange = defaultCollectionMultiplier - minimizedCollectionMultiplier var newMultiplier = collectionViewHeightConstraint.multiplier // Calculate new multiplier based on drag direction and distance if translation.y < 0 { // Upward drag: shrink collection view let dragFraction = min(-translation.y / (viewHeight * multiplierRange), 1.0) newMultiplier = defaultCollectionMultiplier - (multiplierRange * dragFraction) } else { // Downward drag: restore collection view let dragFraction = min(translation.y / (viewHeight * multiplierRange), 1.0) newMultiplier = minimizedCollectionMultiplier + (multiplierRange * dragFraction) } // Clamp multiplier to valid range newMultiplier = max(minimizedCollectionMultiplier, min(defaultCollectionMultiplier, newMultiplier)) // Update constraint without animation for smooth live resizing collectionViewHeightConstraint.isActive = false collectionViewHeightConstraint = collectionView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: newMultiplier) collectionViewHeightConstraint.isActive = true view.layoutIfNeeded() // Optional: Auto-snap to nearest state when gesture ends if gesture.state == .ended || gesture.state == .cancelled { let targetMultiplier = newMultiplier < (defaultCollectionMultiplier + minimizedCollectionMultiplier)/2 ? minimizedCollectionMultiplier : defaultCollectionMultiplier UIView.animate(withDuration: 0.2) { self.collectionViewHeightConstraint.isActive = false self.collectionViewHeightConstraint = self.collectionView.heightAnchor.constraint(equalTo: self.view.heightAnchor, multiplier: targetMultiplier) self.collectionViewHeightConstraint.isActive = true self.view.layoutIfNeeded() } } }
3. Fix Cell Sizing to Eliminate Jank
Your original approach using notifications to update every cell's constraints is a big cause of lag. Instead, let the collection view's layout handle cell sizing automatically based on its current height:
class MainScreenVC: UIViewController { @IBOutlet weak var collectionView: UICollectionView! private let flowLayout = UICollectionViewFlowLayout() override func viewDidLoad() { super.viewDidLoad() collectionView.collectionViewLayout = flowLayout // Set your desired spacing flowLayout.minimumInteritemSpacing = 10 flowLayout.minimumLineSpacing = 10 } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() // Calculate cell size based on current collection view height let cellHeight = collectionView.bounds.height - 20 // Subtract padding if needed let cellWidth = cellHeight * 1.5 // Your aspect ratio flowLayout.itemSize = CGSize(width: cellWidth, height: cellHeight) // Invalidate layout to update cells immediately collectionView.collectionViewLayout.invalidateLayout() } }
Now you can delete all the notification code in your cell class, along with the MyConstant global variable—you don't need them anymore!
4. Key Improvements Recap
- Constraint Multipliers: Aligns with Auto Layout best practices for proportional sizing.
- Pan Gesture Compatibility: Lets users drag to resize while still scrolling the table view.
- Layout-Driven Cell Sizing: Avoids individual cell updates, eliminating lag.
- Cleaner Code: Removes global state and unnecessary notifications for better maintainability.
内容的提问来源于stack exchange,提问作者Aryan Sharma

