Is it possible to use AutoLayout with UITableView's tableHeaderView?

前端 未结 29 1159
醉梦人生
醉梦人生 2020-11-28 19:51

Since I discovered AutoLayout I use it everywhere, now I\'m trying to use it with a tableHeaderView.

I made a subclass of

相关标签:
29条回答
  • 2020-11-28 20:37

    I created a subclass of UITableView and used UIStackView for both header and footer, it also enables setting more than one view.

    https://github.com/omaralbeik/StackableTableView

    0 讨论(0)
  • 2020-11-28 20:38

    For most cases the best solution is simply not to fight the framework and embrace autoresizing masks:

    // embrace autoresizing masks and let the framework add the constraints for you
    headerView.translatesAutoresizingMaskIntoConstraints = true
    headerView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    
    // figure out what's the best size based on the table view width
    let width = self.tableView.frame.width
    let targetSize = headerView.systemLayoutSizeFitting(CGSize(width: width, height: CGFloat.greatestFiniteMagnitude), withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel)
    headerView.frame.size = targetSize
    self.tableView.tableHeaderView = headerView
    

    By using autoresizing masks you're telling the framework how your view should change its size when the superview changes its size. But this change is based on the initial frame you've set.

    0 讨论(0)
  • 2020-11-28 20:38

    I was able to achieve it by the following approach (this works for footer the same way).

    First, you will need small UITableView extension:

    Swift 3

    extension UITableView {
        fileprivate func adjustHeaderHeight() {
            if let header = self.tableHeaderView {
                adjustFrame(header)
            }
        }
    
        private func adjustFrame(_ view: UIView) {
            view.frame.size.height = calculatedViewHeight(view)
        }
    
        fileprivate func calculatedHeightForHeader() -> CGFloat {
            if let header = self.tableHeaderView {
                return calculatedViewHeight(header)
            }
            return 0.0
        }
    
        private func calculatedViewHeight(_ view: UIView) -> CGFloat {
            view.setNeedsLayout()
            let height = view.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
            return height
        }
    }
    

    In your view controller class implementation:

    // this is a UIView subclass with autolayout
    private var headerView = MyHeaderView()
    
    override func loadView() {
        super.loadView()
        // ...
        self.tableView.tableHeaderView = headerView
        self.tableView.sectionHeaderHeight = UITableViewAutomaticDimension
        // ...
    }
    
    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()
    
        // this is to prevent recursive layout calls
        let requiredHeaderHeight = self.tableView.calculatedHeightForHeader()
        if self.headerView.frame.height != requiredHeaderHeight {
            self.tableView.adjustHeaderHeight()
        }
    }
    

    Notes about a header UIView's subview implementation:

    1. You have to 100% sure that your header view has the correct autolayout setup. I would recommend to start with simple header view with just one heigh constraint and try out the setup above.

    2. Override requiresConstraintBasedLayout and return true:

    .

    class MyHeaderView: UIView {
       // ...
       override static var requiresConstraintBasedLayout : Bool {
           return true
       }
       // ...
    }
    
    0 讨论(0)
  • 2020-11-28 20:40

    Share my approach.

    UITableView+XXXAdditions.m

    - (void)xxx_setTableHeaderView:(UIView *)tableHeaderView layoutBlock:(void(^)(__kindof UIView *tableHeaderView, CGFloat *containerViewHeight))layoutBlock {
          CGFloat containerViewHeight = 0;
          UIView *backgroundView = [[UIView alloc] initWithFrame:CGRectZero];
          [backgroundView addSubview:tableHeaderView];
          layoutBlock(tableHeaderView, &containerViewHeight);
    
          backgroundView.frame = CGRectMake(0, 0, 0, containerViewHeight);
    
          self.tableHeaderView = backgroundView;
    }
    

    Usage.

    [self.tableView xxx_setTableHeaderView:myView layoutBlock:^(__kindof UIView * _Nonnull tableHeaderView, CGFloat *containerViewHeight) {
        *containerViewHeight = 170;
    
        [tableHeaderView mas_makeConstraints:^(MASConstraintMaker *make) {
          make.top.equalTo(@20);
          make.centerX.equalTo(@0);
          make.size.mas_equalTo(CGSizeMake(130, 130));
        }];
      }];
    
    0 讨论(0)
  • 2020-11-28 20:40

    Here's what works for UITableViewController in ios 12,

    Drap a UIView into the TableView above all the prototype cells for header and below all the prototype cells for footer. Setup your header and footer as needed. Set all the required constraints.

    Now use the following extension methods

    public static class UITableVIewExtensions
    {
    
        public static void MakeHeaderAutoDimension(this UITableView tableView)
        {
            if (tableView.TableHeaderView is UIView headerView) {
                var size = headerView.SystemLayoutSizeFittingSize(UIView.UILayoutFittingCompressedSize);
                if (headerView.Frame.Size.Height != size.Height) {
                    var frame = headerView.Frame;
                    frame.Height = size.Height;
                    headerView.Frame = frame;
                    tableView.TableHeaderView = headerView;
                    tableView.LayoutIfNeeded();
                }
            }
        }
    
        public static void MakeFooterAutoDimension(this UITableView tableView)
        {
            if (tableView.TableFooterView is UIView footerView) {
                var size = footerView.SystemLayoutSizeFittingSize(UIView.UILayoutFittingCompressedSize);
                if (footerView.Frame.Size.Height != size.Height) {
                    var frame = footerView.Frame;
                    frame.Height = size.Height;
                    footerView.Frame = frame;
                    tableView.TableFooterView = footerView;
                    tableView.LayoutIfNeeded();
                }
            }
        }
    }
    

    and call it in ViewDidLayoutSubviews of the subclass of UITableViewController

    public override void ViewDidLayoutSubviews()
    {
        base.ViewDidLayoutSubviews();
    
        TableView.MakeHeaderAutoDimension();
        TableView.MakeFooterAutoDimension();
    }
    
    0 讨论(0)
  • 2020-11-28 20:41

    Any constraint-based UIView can be a good tableHeaderView.

    One needs to set a tableFooterView before and then impose additional trailing constraint on tableFooterView and tableHeaderView.

    - (void)viewDidLoad {
    
        ........................
        // let self.headerView is some constraint-based UIView
        self.tableView.tableFooterView = [UIView new];
        [self.headerView layoutIfNeeded];
        self.tableView.tableHeaderView = self.headerView;
    
        [self.tableView.leadingAnchor constraintEqualToAnchor:self.headerView.leadingAnchor].active = YES;
        [self.tableView.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor].active = YES;
        [self.tableView.topAnchor constraintEqualToAnchor:self.headerView.topAnchor].active = YES;
        [self.tableFooterView.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor].active = YES;
    

    }

    One can find all details and code snippets here

    0 讨论(0)
提交回复
热议问题