UITableViewCell子视图布局时机及动态StackView提示视图定位咨询
Hey there! Let's work through this layout problem for your tip view. The core issue here is getting the final position of your dynamic-width stack view without dealing with the flickering from repeated layoutSubviews calls or inaccurate frames in willDisplayCell.
Recommended Solution: Use didLayoutSubviews on the Cell + One-Time Constraint Setup
Here's a clean approach that ensures you get the stack view's final position and avoids unnecessary updates:
Notify the parent controller when the cell's stack view finishes layout
In your customUITableViewCellsubclass, overridedidLayoutSubviewsto send a notification once the stack view has settled into its final frame:- (void)didLayoutSubviews { [super didLayoutSubviews]; // Skip if the stack view hasn't been laid out yet if (CGRectEqualToRect(self.stackView.bounds, CGRectZero)) { return; } // Send a notification to let the parent know we're ready [[NSNotificationCenter defaultCenter] postNotificationName:@"StackViewLayoutFinished" object:self]; }Update the tip view constraints in the parent controller
In your view controller, listen for the notification and set/update the tip view's constraints only when needed (to avoid flickering from repeated changes):- (void)viewDidLoad { [super viewDidLoad]; // Listen for the stack layout notification [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateTipViewLayout:) name:@"StackViewLayoutFinished" object:nil]; // Set one-time constraints (trailing/bottom) here instead of re-setting them every time self.tipViewTrailingConstraint = [self.tipView.trailingAnchor constraintLessThanOrEqualToAnchor:self.view.trailingAnchor constant:-marginConstant]; self.tipViewBottomConstraint = [self.tipView.bottomAnchor constraintEqualToAnchor:self.tableView.bottomAnchor constant:-TSkBubbleClickAreaHeight-marginConstant]; [NSLayoutConstraint activateConstraints:@[self.tipViewTrailingConstraint, self.tipViewBottomConstraint]]; } - (void)updateTipViewLayout:(NSNotification *)notification { UITableViewCell *targetCell = notification.object; // Make sure this is the cell you want to align the tip view to (e.g., selected cell) if (targetCell != self.currentActiveCell) { return; } // Get the final stack view position relative to the parent view UIView *parentView = self.view; CGRect stackViewGlobalRect = [targetCell.stackView convertRect:targetCell.stackView.bounds toView:parentView]; // Update the leading constraint (remove old one first if it exists) if (self.tipViewLeadingConstraint) { [self.tipViewLeadingConstraint setActive:NO]; } self.tipViewLeadingConstraint = [self.tipView.leadingAnchor constraintEqualToAnchor:parentView.leadingAnchor constant:stackViewGlobalRect.origin.x]; [self.tipViewLeadingConstraint setActive:YES]; // Trigger immediate layout to apply changes [parentView layoutIfNeeded]; } - (void)dealloc { // Clean up the notification observer [[NSNotificationCenter defaultCenter] removeObserver:self]; }
Why This Works
didLayoutSubviewson the cell is called after all subviews (including the dynamic stack view) have finished laying out, so you get the final, accurate frame position.- By using a notification, you only update the tip view's constraints when the stack view actually finishes its layout, avoiding the repeated calls and flickering from
layoutSubviews. - Storing references to your constraints lets you update them cleanly without adding duplicate constraints.
Alternative Quick Fix (Less Elegant)
If you don't want to use notifications, you can add a small delay in your view controller's viewDidLayoutSubviews to ensure the cell has finished laying out:
- (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; // Delay to let the cell's layout settle dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self updateTipViewLayoutForCurrentCell]; }); } - (void)updateTipViewLayoutForCurrentCell { // Same constraint update logic as above }
This works but relies on a arbitrary delay, so the notification method is more reliable.
内容的提问来源于stack exchange,提问作者ArgenBarbie

