Search code examples
iosswiftuitableviewuikituitextfield

Programatically adding UITextField above UITableView


I'm trying to make a search application using MusicBrainz API, where the API will return JSON data that matches the search term typed in by the user.

This is what I have so far of my UI:

import UIKit
import WebKit

class ArtistListViewController: UIViewController{
 
    let tableView = UITableView()
    var safeArea: UILayoutGuide!
    var artists: [Artists]?
    
    let textField = UITextField()
    var searchTerm = "Search"
    
    var webView: WKWebView!
 
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        safeArea = view.layoutMarginsGuide
        
        searchBar()
        setUpTable()
        setUpNavigation()
  
    }
    
    func searchBar(){
        view.addSubview(textField)
        
        textField.placeholder = "Search"
        textField.frame = CGRect(x: 10,y: 200,width: 300.0,height: 30.0)
        
        textField.borderStyle = UITextField.BorderStyle.line

        textField.translatesAutoresizingMaskIntoConstraints = false
        
        
        //Layout Configs
        textField.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
        
        textField.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
        textField.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
        
        textField.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true

 }
    
    func setUpTable(){
        view.addSubview(tableView)
        
        ArtistSearchModelData().loadArtists(searchTerm: "Adele"){ [weak self] (artists) in
              self?.artists = artists
              
              DispatchQueue.main.async{
                self?.tableView.reloadData()
              }
        }
        
        //populate with data
        tableView.delegate = self
        tableView.dataSource = self
        tableView.register(TableViewCell.self, forCellReuseIdentifier: "cell")
        
        
        //turn off autoresizing
        tableView.translatesAutoresizingMaskIntoConstraints = false
        
        //Layout Configs
        tableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
        tableView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
        tableView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
        tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
       
    }
    
    func setUpNavigation(){
        self.navigationItem.title = "Artists"
        self.navigationController?.navigationBar.barTintColor = .white
        self.navigationController?.navigationBar.isTranslucent = false
        self.navigationController?.navigationBar.titleTextAttributes = [
            NSAttributedString.Key.foregroundColor: UIColor.orange,
            NSAttributedString.Key.font: UIFont(name: "Arial-BoldMT", size: 30)
        ]
    }

}

This is what my UI looks like:

enter image description here

As you can see my search bar is completely missing and I have no idea how to render it.

I tried using a UIStackView but got the same results.

I've tried searching the internet and found similar solutions but couldn't get any of them to work.

Adding both textField and tableView to a custom subview renders nothing too, maybe because they're functions? Am I just going about this the wrong way?

Any help is appreciated!


Solution

  • You constrain your text field to the top of the view:

    textField.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
    

    then, you constrain your table view to the top of the view:

    tableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
    

    So your table view is covering your text field.

    You could do this:

    tableView.topAnchor.constraint(equalTo: textField.bottomAnchor).isActive = true
    

    To constrain the Top of the table view to the Bottom of the text field.

    As a side note, you should constrain to the view's Safe Area ... not to the view itself:

    textField.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
    

    Edit

    Here is your class with the above modifications (note that I commented-out the stuff I don't have access to, such as your Artist specific code):

    class ArtistListViewController: UIViewController{
        
        let tableView = UITableView()
        var safeArea: UILayoutGuide!
        //var artists: [Artists]?
        
        let textField = UITextField()
        var searchTerm = "Search"
        
        //var webView: WKWebView!
        
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view.
            
            safeArea = view.safeAreaLayoutGuide
            
            searchBar()
            setUpTable()
            setUpNavigation()
            
        }
        
        func searchBar(){
            
            view.addSubview(textField)
            
            textField.placeholder = "Search"
            
            // not needed
            //textField.frame = CGRect(x: 10,y: 200,width: 300.0,height: 30.0)
            
            textField.borderStyle = UITextField.BorderStyle.line
            
            textField.translatesAutoresizingMaskIntoConstraints = false
            
            
            //Layout Configs
            
            // constrain Top to safeArea Top
            textField.topAnchor.constraint(equalTo: safeArea.topAnchor).isActive = true
            
            textField.leftAnchor.constraint(equalTo: safeArea.leftAnchor).isActive = true
            textField.rightAnchor.constraint(equalTo: safeArea.rightAnchor).isActive = true
            
            // don't constrain the bottom
            //textField.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
            
        }
        
        func setUpTable(){
            view.addSubview(tableView)
            
    //      ArtistSearchModelData().loadArtists(searchTerm: "Adele"){ [weak self] (artists) in
    //          self?.artists = artists
    //
    //          DispatchQueue.main.async{
    //              self?.tableView.reloadData()
    //          }
    //      }
    //
    //      //populate with data
    //      tableView.delegate = self
    //      tableView.dataSource = self
    //      tableView.register(TableViewCell.self, forCellReuseIdentifier: "cell")
            
            
            //turn off autoresizing
            tableView.translatesAutoresizingMaskIntoConstraints = false
            
            //Layout Configs
            
            // constrain Top to textField Bottom
            tableView.topAnchor.constraint(equalTo: textField.bottomAnchor).isActive = true
            
            tableView.leftAnchor.constraint(equalTo: safeArea.leftAnchor).isActive = true
            tableView.rightAnchor.constraint(equalTo: safeArea.rightAnchor).isActive = true
            tableView.bottomAnchor.constraint(equalTo: safeArea.bottomAnchor).isActive = true
            
        }
        
        func setUpNavigation(){
            self.navigationItem.title = "Artists"
            self.navigationController?.navigationBar.barTintColor = .white
            self.navigationController?.navigationBar.isTranslucent = false
            self.navigationController?.navigationBar.titleTextAttributes = [
                NSAttributedString.Key.foregroundColor: UIColor.orange,
                NSAttributedString.Key.font: UIFont(name: "Arial-BoldMT", size: 30)
            ]
        }
        
    }
    

    If you run that code as-is, you should get your Search textField above the (empty) tableView.

    If you then un-comment your Artist-specific code, it should work properly.