简体   繁体   中英

How do I adjust my popover to the size of the content in my tableview in swift?

I'm using popoverPresentationController to show my popover. The UITableViewController used to show as popover is created programmatically and will usually contain 1 to 5 rows. How do I set up this popover to adjust the size to the content of the tableview?

Code for my popover:

if recognizer.state == .Began {
    let translation = recognizer.locationInView(view)

    // Create popoverViewController
    var popoverViewController = UITableViewController()
    popoverViewController.modalPresentationStyle = UIModalPresentationStyle.Popover
    popoverViewController.tableView.backgroundColor = UIColor.popupColor()

    // Settings for the popover
    let popover = popoverViewController.popoverPresentationController!
    popover.delegate = self
    popover.sourceView = self.view
    popover.sourceRect = CGRect(x: translation.x, y: translation.y, width: 0, height: 0)
    popover.backgroundColor = UIColor.popupColor()

    presentViewController(popoverViewController, animated: true, completion: nil)
}

In your UITableViewController's viewDidLoad() you can add an observer:

self.tableView.addObserver(self, forKeyPath: "contentSize", options: .new, context: nil)

Then add this method:

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    self.preferredContentSize = tableView.contentSize
}

Lastly, in viewDidDisappear() , make sure you remove the observer:

tableView.removeObserver(self, forKeyPath: "contentSize")

This way the popover will automatically adjust size to fit the content, whenever it is loaded, or changed.

Checkout the preferredContentSize property of UIViewController :

let height = yourDataArray.count * Int(popOverViewController.tableView.rowHeight)
popOverViewController.preferredContentSize = CGSize(width: 300, height: height)

Override the preferredContentSize property in your extension of the uitableviewcontroller as following:

override var preferredContentSize: CGSize {
    get {
        let height = calculate the height here....
        return CGSize(width: super.preferredContentSize.width, height: height)
    }
    set { super.preferredContentSize = newValue }
}

For calculating the height check out tableView.rectForSection(<#section: Int#>)

Simple dynamic answer for Swift 4.x and Swift 5.x involving no size-computation (modern version of Bo Frese answer):

private var contentSizeObserver : NSKeyValueObservation?

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    contentSizeObserver = tableView.observe(\.contentSize) { [weak self] tableView, _ in
        self?.preferredContentSize = CGSize(width: 320, height: tableView.contentSize.height) // Here I fixed the width but you can do whatever you want
    }
}

override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)

    contentSizeObserver?.invalidate()
    contentSizeObserver = nil
}

For swift 4 if you want to observe the content size I found this to be the optimal solution. Reporting it here since I did not find a complete example online:

class MyTableViewController: UITableViewController {

    private var kvoContext = 0

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        addObserver(self, forKeyPath: #keyPath(tableView.contentSize), options: .new, context: &kvoContext)

    }

    override func viewDidDisappear(_ animated: Bool) {
        removeObserver(self, forKeyPath: #keyPath(tableView.contentSize))
        super.viewDidDisappear(animated)
    }

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if context == &kvoContext, keyPath == #keyPath(tableView.contentSize),
            let contentSize = change?[NSKeyValueChangeKey.newKey] as? CGSize  {
            self.popoverPresentationController?.presentedViewController.preferredContentSize = contentSize
        }
    }
}

First thing first: All comments are good and help full. I have did little change in my logic which makes my VC as reusable component.

Calling this method inside viewWillAppear:(BOOL)animated :

-(void) setPopOverPreferedContentHeight {

     if (self.popoverPresentationController && self.tableView.contentSize.height < MAX_POPOVER_HEIGHT) {
          self.preferredContentSize=self.tableView.contentSize;
     } else if (self.popoverPresentationController){
         self.preferredContentSize = CGSizeMake(self.tableView.contentSize.width, MAX_POPOVER_HEIGHT);
   }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM