When is the layout completed for a custom UITableViewCell?

I have a custom table cell defined in the storyboard file.

enter image description here

This cell contains a scroll view with multiple pages of content and a page indicator. Scrolling, browsing, and the cell itself are determined using automatic layout. Page 1/2/3 views are snapped to the same width as the scroll, and container bindings are 3 times wide in the scroll view. Thus, I'm not sure if the content is exactly sized to display three pages of information regardless of screen size.

I have a code in a custom cell class to refresh the page indicator when the scroll has looked at the scroll - this works fine.

I also set a method in the cell class to specify the page from the outside, and in this setter I am doing something like this:

- (void)setPage:(NSInteger)page animated:(BOOL)animated { CGFloat pageWidth = self.scrollView.frame.size.width; self.pageControl.currentPage = page; [self.scrollView setContentOffset:CGPointMake(page*pageWidth, 0) animated:animated]; } 

The problem I am encountering is quite often viewing the viewing frame, and the size of the content is not properly determined when I call this setter, so the page indicator will be set, but the scroll view does not change the content offset.

Where can I catch the moment when my automatic cell layout is complete and I can guarantee that setContentOffset will work? I try to call setPage from cellForIndexPath - this obviously does not work, then I tried to call it from willDisplayCell - the same plot - the cell itself is already laid out correctly, but the scroll view still has 600px (from the storyboard), and this fails.

+6
source share
3 answers

It seems you are set to custom-made implementation, so this might work.

Calling the -setPage:animated: method before the table view -setPage:animated: its children, giving the cell the ability to do the same, or the automatic maneuver will not work if you do not support any state in the cell (perhaps only the page number) and respond to one or more of the following possibilities:

  • Waiting for the -viewDidLayoutSubviews method of your UITableViewController to -viewDidLayoutSubviews , then call -cellForRowAtIndexPath: to get the cell. Then the cell must have a valid frame.

  • Override -didMoveToSuperview in a custom UITableViewCell. By the time this is called, the frame must also be valid.

After any of these points, you can check your custom cell state (which should contain the page number that should be displayed) and scroll to the corresponding point. It would probably be advisable to reorganize your method to set the state (an integer property in the cell) and have a separate private method, which is -setPage:animated: and one of the two labels above can cause offset calculation and scrolling.


Recommended Alternative:

You can remove this by inserting a UICollectionView into the contentView cell. Your UIViewController, which currently implements a UITableViewDataSource, can also implement a UICollectionViewDataSource and provide a collection view of its cells.

Assuming your UITableView cells are the width and height of the screen, you can configure the inline collection view mostly horizontally (since UICollectionView is a subclass of UIScrollView).

When setting up the collection view, use UICollectionViewFlowLayout as the layout object and configure it like this:

 // Ensure the collection view scrolls horizontally. let flowLayout = UICollectionViewFlowLayout(); flowLayout.scrollDirection = .Horizontal; let collectionView = UICollectionView(frame:tableCellFrame, layout:flowLayout); // These are actually UIScrollView properties. collectionView.pagingEnabled = true; collectionView.bounces = false; 

Then you put each page of content in a custom UICollectionViewCell, and the collection view will paginate them for you. This gives an added bonus to provide you with a scrollable scroll API for free.

+1
source

I ended up doing the following:

Added layoutSubview implementation of layoutSubview in custom cell subclass

 - (void)layoutSubviews { [super layoutSubviews]; [self.contentView layoutSubviews]; // set proper content offset [self.scrollView setContentOffset:CGPointMake(self.frame.size.width*self.pageControl.currentPage, 0) animated:NO]; } 

Without a direct call to layoutSubview in the contentView scroll did not have the correct frame even after [super layoutSubview] .

I did not need to store the current page number in an internal variable, since pageControl already holds it, and it is set when setting up the initial information about the cell.

What was interesting - I originally added [self.scrollView layoutSubview] inside this method, and it worked fine in iOS8 but did nothing in iOS7. I had to go up one level to the contentView to make sure that it works in both 7 and 8.

+15
source

Since layoutSubviews () is often called multiple times, it is hard to see if the layout is final. So I implemented an extension for UIView and use it to find out which layoutSubviews () call should be made.

Swift 4.2

 extension UIView { /** A Boolean value that determines whether the view or any of its subviews need a layout update. This can be called from -layoutSubviews to determine if the layout of all the subviews are up-to-date. */ open var needsLayout: Bool { get { return needsLayout(view: self) } } func needsLayout(view: UIView) -> Bool { var result = view.needsUpdateConstraints() for subview in view.subviews { result = result || needsLayout(view: subview) } return result } } 

Then it can simply be used in a subclass of UITableViewCell:

 class MyCustomCell: UITableViewCell { override func layoutSubviews() { super.layoutSubviews() if !needsLayout { // The layout is now complete, do what you must! } } 
0
source

Source: https://habr.com/ru/post/1207349/


All Articles