Henny Lee
Henny Lee

Reputation: 3062

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)
}

Upvotes: 42

Views: 35654

Answers (6)

Zaphod
Zaphod

Reputation: 7290

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
}

Upvotes: 14

Daniele Bernardini
Daniele Bernardini

Reputation: 1536

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
        }
    }
}

Upvotes: 3

kaushal
kaushal

Reputation: 1573

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);
   }
}

Upvotes: 0

Bo Frese
Bo Frese

Reputation: 903

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.

Upvotes: 49

zisoft
zisoft

Reputation: 23078

Checkout the preferredContentSize property of UIViewController:

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

Upvotes: 29

Philip De Vries
Philip De Vries

Reputation: 415

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#>)

Upvotes: 20

Related Questions