Adam Stoller
Adam Stoller

Reputation: 933

How to place toolbar above (or below) scrolling tableview

ENVIRONMENT:

OBJECTIVE: STORYBOARD: CURRENT RESULTS: QUERY:
  • Can I accomplish what I want from within the Xcode UI?
    • If so, can someone provide some pointers / steps that I need to take to do so?
  • Or do I need to do this within the Swift code?
    • Again, pointers / steps / generic code example gratefully appreciated.



  • WRT 2016-09-15 Edit in Answer #1

    Embed the initial View Controller (the one with the buttons) in an `UINavigationController`.

    I don't see how to do that, as the only thing I seem to be able to embed within the navigation controller is a Tab Bar Controller

    If not already created, make a `UITableViewController` class and link it to your Table View Controller.

    I already have class ExistingLocationsViewController: UITableViewController { ... } (and a class ExistingLocationTableViewCell: UITableViewCell { ... }) linked, I believe, appropriately

    Add this code to the class in order to make the Toolbar appear and disappear when you are viewing the Table View:
        import UIKit
        class ViewController: UITableViewController {
            override func viewDidLoad() {
                super.viewDidLoad()
                navigationController?.isToolbarHidden = false
            }
            override func viewWillDisappear(_ animated: Bool) {
                navigationController?.isToolbarHidden = true
            }
        }
    This should be all you'll have to do. If you want to edit the toolbar make it visible while you are editing it and afterwards hide it again.

    FYI, in my Xcode, iOS version, the property appears to be named toolbarHidden (not **isT**oolbarHidden)


    ANOTHER APPROACH (2016-09-17)
    Going back a step (or two), I noticed an option with the Table View Controller and tried to make use of it, but it didn't seem to work. See image (and text within) for details:

    When I run the app, it doesn't seem to make any difference (again, see image):

    Am I doing something wrong?, or completely misunderstanding the Xcode interface?

    Upvotes: 3

    Views: 4826

    Answers (2)

    Adam Stoller
    Adam Stoller

    Reputation: 933

    RESOLUTION (with much thanks to Christoph!)

    1. Preliminary Work

      • My work began with a Single Page app consisting of the default UIViewController (ViewController.swift).
      • I added buttons to the view (and tweaked their appearance and such)
      • I added a UITableViewController and created my own custom class for it (MyTableViewController.swift)
      • In the table's Prototype Cells I added a couple of labels to populate with the data, and created my own custom class for the cells (MyTableViewCell.swift)
      • I then created segues from the buttons in the initial UIViewController to the table in the UITableViewController
        • Each segue has a unique Identifier (this will be used later, but for now it's to allow the id to be passed from the initial view to the table view)

          At this point, the app "worked", but I wanted to have some buttons (either at the top or bottom of the table) to act on whatever data was selected within the table. When I started this thread, I had added a Toolbar, but it basically pinned itself to just below the prototype cell and when there were more rows of data than would fit on a single screen, it too was scrolled off and didn't appear until the list was scrolled all the way to the end.
    2. Fix - a.k.a. Christoph to the Rescue

      • Start by embedding the initial view within a Navigation Controller
        • I was initially confused about what was getting embedded into what, hopefully this picture makes it much clearer
      • The result of doing this, modifies the Storyboard thusly:
      • I then went to My Table View Controller Scene and inserted a Navigation Item into the space that now showed up at the top of that view:
      • To this, I was able to add Bar Button Items:
      • And with a variety of custom code (more on this below) was able to get this:
    3. Code

    • At this point, the code for `ViewController.swift` is relatively simple:
    import UIKit
    
    class ViewController: UIViewController {
    
        @IBOutlet weak var b1: UIButton!
        @IBOutlet weak var b2: UIButton!
        @IBOutlet weak var b3: UIButton!
        var buttonId: String = ""
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view, typically from a nib.
            b1.titleLabel?.adjustsFontSizeToFitWidth = true
            b2.titleLabel?.adjustsFontSizeToFitWidth = true
            b3.titleLabel?.adjustsFontSizeToFitWidth = true
        }
    
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
            // Dispose of any resources that can be recreated.
        }
    
        override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
            let destination      = segue.destinationViewController as! MyTableViewController
            destination.buttonId = segue.identifier
        }
    }
    
    • The code for `MyTableViewCell.swift` is also pretty simple (at least for now):
    import UIKit
    
    class MyTableViewCell: UITableViewCell {
        @IBOutlet weak var l1: UILabel!
        @IBOutlet weak var l2: UILabel!
    
        override func awakeFromNib() {
            super.awakeFromNib()
    
            // Add bordering to bottom of each cell
            let border = CALayer()
            let width = CGFloat(1.0)
            border.borderColor = UIColor.darkGrayColor().CGColor
            border.frame = CGRect(x: 0, y: self.frame.size.height - width, width:  self.frame.size.width, height: self.frame.size.height)
            border.borderWidth = width
            self.layer.addSublayer(border)
        }
    
        override func setSelected(selected: Bool, animated: Bool) {
            super.setSelected(selected, animated: animated)
        }
    }
    
    • The code for `MyTableViewController.swift` is, on the other hand, more complex - perhaps because there's just more of it:
    import UIKit
    
    class MyTableViewController: UITableViewController {
        @IBOutlet weak var b1: UIBarButtonItem!
        @IBOutlet weak var b2: UIBarButtonItem!
        var buttonId: String!
        var oldRow = -1
        // The data below is clearly just for testing, eventually this should be pulled from file or DB
        var locationList: [LocationObject] = [
            LocationObject(name: "name-01", address: "addr-1", selected: false),
            LocationObject(name: "name-02", address: "addr-2", selected: false),
            LocationObject(name: "name-03", address: "addr-3", selected: false),
            LocationObject(name: "name-04", address: "addr-3", selected: false),
            LocationObject(name: "name-05", address: "addr-3", selected: false),
            LocationObject(name: "name-06", address: "addr-3", selected: false),
            LocationObject(name: "name-07", address: "addr-3", selected: false),
            LocationObject(name: "name-08", address: "addr-3", selected: false),
            LocationObject(name: "name-09", address: "addr-3", selected: false),
            LocationObject(name: "name-10", address: "addr-3", selected: false),
            LocationObject(name: "name-11", address: "addr-1", selected: false),
            LocationObject(name: "name-12", address: "addr-2", selected: false),
            LocationObject(name: "name-13", address: "addr-3", selected: false),
            LocationObject(name: "name-14", address: "addr-3", selected: false),
            LocationObject(name: "name-15", address: "addr-3", selected: false),
            LocationObject(name: "name-16", address: "addr-3", selected: false),
            LocationObject(name: "name-17", address: "addr-3", selected: false),
            LocationObject(name: "name-18", address: "addr-3", selected: false),
            LocationObject(name: "name-19", address: "addr-3", selected: false),
            LocationObject(name: "name-10", address: "addr-3", selected: false),
            ]
    
        // This embedded class is to support the above data. It may get replaced in final code
        class LocationObject {
            var name:     String!
            var address:  String!
            var selected: Bool!
    
            init(name: String, address: String, selected: Bool) {
                self.name      = name
                self.address   = address
                self.selected  = false
            }
        }
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // code to hide/expose the navbar (top) and toolbar(bottom) when the view loads
            navigationController?.navigationBarHidden = false 
            navigationController?.toolbarHidden       = true
        }
    
       // code to hide the navbar and toolbar when this view goes away
        override func viewWillDisappear(animated: Bool) {
            navigationController?.navigationBarHidden = true
            navigationController?.toolbarHidden       = true 
        }
    
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
        }
    
        override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
            return 1
        }
    
        override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return locationList.count
        }
    
        override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCellWithIdentifier("resultCell", forIndexPath: indexPath) as! MyTableViewCell
            let row  = indexPath.row
            let item = locationList[row]
    
            cell.l1.text = item.name
            cell.l2.text = item.address
    
            return cell
        }
    
        override func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
            let cell                   = tableView.cellForRowAtIndexPath(indexPath) as! MyTableViewCell
            let row                    = indexPath.row
            cell.accessoryType         = .None
            locationList[row].selected = false
        }
    
        override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
            b1.enabled = true
            let cell                   = tableView.cellForRowAtIndexPath(indexPath) as! MyTableViewCell
            let row                    = indexPath.row
            locationList[row].selected = !locationList[row].selected
            cell.accessoryType         = (locationList[row].selected == true) ? .Checkmark : .None
            cell.highlighted           = false
            cell.setHighlighted(false, animated: true)
    
            // TOGGLE SELECTION
            if oldRow == row {
                b1.enabled = false
                let oldIndexPath = NSIndexPath(forRow: oldRow, inSection: 0)
                tableView.deselectRowAtIndexPath(oldIndexPath, animated: true)
                oldRow = -1
            }
            else {
                oldRow = row
            }
        }
    }
    

    And that, in a nutshell is it. I'm still in the early stages of developing my app - somehow I'm reasonably sure I'll be posting some more questions regarding the implementation as I move forward, but hopefully the above will help others who might be struggling with the same, or similar, issue(s).

    Thanks again to Christoph for providing the much needed directions regarding embedding the view into a navigation view controller and such.

    Upvotes: 1

    Christoph
    Christoph

    Reputation: 722

    One of the easiest ways to add a toolbar to any view is by embedding it in a NavigationViewController. All this can be done without code:

    1. Take the View Controller that you currently have (I assume the UITableViewController) and under Editor -> Embed In select Navigation Controller. enter image description here

      1. Select the newly created Navigation Controller. In the utilities bar under Bar Visibility, select Toolbar to be shown. If needed you can also keep the Navigation Bar, else turn it off. enter image description here

      2. Now you are able to modify the Toolbar in UITableViewController. Just drag the different Toolbar items (Bar Button Item, Fixed Space Bar Button Item, Flexible Bar Button Item) onto the Toolbar as you please. Be aware, you can't move the toolbar and you can only change its style in the NavigationViewController. enter image description here

    EDIT: Since you have a different initial View Controller, I would recommend you solving the issue slightly differently. This will require a little bit of code:

    1. Embed the initial View Controller (the one with the buttons) in an UINavigationController. Remove any other Navigation Controllers. Your buttons should simply have a segue that show the UITableViewController. Disable the Toolbar in the Navigation Controller and as before, it's up to you to enable the Navigation Bar (I would recommend it). enter image description here

    2. If not already created, make a UITableViewController class and link it to your Table View Controller. enter image description here

    3. Add this code to the class in order to make the Toolbar appear and disappear when you are viewing the Table View:

      import UIKit
      
      class ViewController: UITableViewController {
      
          override func viewDidLoad() {
              super.viewDidLoad()
              navigationController?.isToolbarHidden = false
          }
      
          override func viewWillDisappear(_ animated: Bool) {
              navigationController?.isToolbarHidden = true
          }
      
      }
      

    This should be all you'll have to do. If you want to edit the toolbar make it visible while you are editing it and afterwards hide it again.

    Upvotes: 5

    Related Questions