Cannot set delegate to reload table view - swift

I am trying to reload a table view with the help of a delegate. I found tons of examples here on stack overflow, but I always end up with an error.
My first controller which should update the table view:
import Cocoa
class ViewController: NSViewController {
#IBOutlet weak var tableView: NSTableView!
var tableViewData: [[String:String]] = []
override func viewDidLoad() {
super.viewDidLoad()
...
self.tableView.delegate = self as NSTableViewDelegate
self.tableView.dataSource = self
self.tableView.reloadData()
}
override var representedObject: Any? {
didSet {
}
}
func reloadTableData(_ notification: Notification) {
tableView.reloadData()
}
}
extension ViewController: NSTableViewDataSource, NSTableViewDelegate {
func numberOfRows(in tableView: NSTableView) -> Int {
return tableViewData.count
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView?{
var result:CustomTableCellview
result = tableView.make(withIdentifier: (tableColumn?.identifier)!, owner: self) as! CustomTableCellview
result.textField?.stringValue = tableViewData[row][(result.textField?.identifier!)!]!
result.secondTextField?.stringValue = tableViewData[row][result.secondTextField.identifier!]!
return result
}
}
extension ViewController: PageControllerDelegate {
func updateTableData() {
tableView.reloadData()
}
}
My second controller, which should tell the first one it can update the table view:
import Cocoa
protocol PageControllerDelegate {
func updateTableData()
}
class PageController: NSPageController {
var delegate: PageControllerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func saveData(sender: NSButton) {
...
delegate?.updateTableData()
self.dismiss(self)
}
}
Within the PageController I get the following error:
Property 'delegate' with type 'PageControllerDelegate?' cannot override a property with type 'NSPageControllerDelegate?' (aka 'Optional<NSPageControllerDelegate>')

Rename the delegate protocol name to something like MyPageControllerDelegate. It seems like there is already something called PageControllerDelegate that is defined either by you, or Apple

NSPageController already has a property delegate of type NSPageControllerDelegate. Remove var delegate: PageControllerDelegate?.

Related

how to get check box functionality in tableview footer view

i have to use checkbox in tableview footer, on check i have to display textfield.
in my ViewController
#IBOutlet weak var resultTable: UITableView!
in FooterCell
import UIKit
class FooterCell: UITableViewCell
{
#IBOutlet weak var textFld: UITextField!
#IBOutlet weak var checkBtn: UIButton!
#IBOutlet weak var submitBtn: UIButton!
override func awakeFromNib()
{
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool)
{
super.setSelected(selected, animated: animated)
}
}
this is my code in class
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView?
{
let footer = resultTable.dequeueReusableCell(withIdentifier: "footer") as? FooterCell
footer!.textFld.isHidden = true
footer!.submitBtn.isHidden = true
footer!.checkBtn.addTarget(self, action: #selector(checkBoxSelection(_:)), for: .touchUpInside)
return footer?.contentView
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat
{
return 55
}
#objc func checkBoxSelection(_ sender:UIButton)
{
let footer = resultTable.dequeueReusableCell(withIdentifier: "footer") as? FooterCell
if askBool
{
footer!.checkBtn.setImage(UIImage(named:"CheckBox"), for: .normal)
footer!.textFld.isHidden = false
footer!.submitBtn.isHidden = false
askBool = false
} else
{
footer!.checkBtn.setImage(UIImage(named:"UnCheckBox"), for: .normal)
footer!.textFld.isHidden = true
footer!.submitBtn.isHidden = true
askBool = true
}
}
If you would like to add functionality to your footerView you need to use protocol to detect user has checked or unchecked button.
1) First, create a protocol
protocol FooterCheckable: class {
func isChecked()
}
2) Then inside your footer view class do following inside checkbox action.
class TableViewFooter: UITableViewHeaderFooterView {
weak var delegate: FooterCheckable?
#IBAction func approveOrRejectBtnPressed(_ sender: UIButton) {
delegate?.isChecked()
}
}
3) Inside your view controller you need to register your tableViewFooter with following code
tableView.register(UINib(nibName: yourFooterViewString, bundle: nil),
forHeaderFooterViewReuseIdentifier: identifier)
4) Then, again inside your View Controller class you need to have following code to use header and give delegate to your view controller
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
if let footer = tableView.dequeueReusableHeaderFooterView(withIdentifier: identifier) as? TableViewFooter {
footer.delegate = self
return footer
}
return nil
}
5) Lastly, you need to be sure you need to conform your protocol inside the view controller and do whatever you need when header button checked.
extension ViewController: FooterCheckable {
func isChecked() {
}
}
That is happy ending hope it will be useful for you.

not able to load data with ViewModel

the tableView dataSource is properly set up in the IB
the viewController identity is properly set as well in the IB
this is my viewModel
class StatusCodeViewModel {
let apiClient = APIClient.shared
var statusCodes: [StatusCode] = []
let identifier = "statusCodeCell"
init() {}
func loadStatusCodes() {
apiClient.execute(service: .statusCode) { statusCodes in
self.statusCodes = statusCodes
}
}
}
and the viewController in which I want to load data
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var viewModel: StatusCodeViewModel? {
didSet {
if viewModel!.statusCodes.count > 0 {
self.tableView.reloadData()
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
viewModel = StatusCodeViewModel()
viewModel!.loadStatusCodes()
}
}
extension ViewController : UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let statusCodes = viewModel!.statusCodes as? [StatusCode] {
return statusCodes.count
}
return 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: viewModel!.identifier)
cell?.textLabel!.text = viewModel!.statusCodes[indexPath.row].title
return cell!
}
}
the data count is 0 and no data is shown in the tableView
You have did set on view model which will occur on initialisation.
You will have to implement some kind of callback when the api returns the call - easiest way would be protocol.
protocol StatusCodeViewModelDelegate {
func callFinished()
}
class StatusCodeViewModel {
let apiClient = APIClient.shared
var statusCodes: [StatusCode] = []
let identifier = "statusCodeCell"
var delegate : StatusCodeViewModelDelegate?
init() {}
func loadStatusCodes() {
apiClient.execute(service: .statusCode) { statusCodes in
self.statusCodes = statusCodes
delegate?.callFinished()
}
}
}
Then in your viewController:
override func viewDidLoad() {
super.viewDidLoad()
viewModel = StatusCodeViewModel()
viewModel.delegate = self
viewModel!.loadStatusCodes()
}
func callFinished() {
self.tableView.reloadData()
}
Don't forget to extend for delegate you just made:
class ViewController: UIViewController, StatusCodeViewModelDelegate {
Or, as #rmaddy suggested, in View model change loadStatusCodes to:
func loadStatusCodes(completion: #escaping () -> Void) {
apiClient.execute(service: .statusCode) { statusCodes in
self.statusCodes = statusCodes
}
}
Then, in the viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
viewModel = StatusCodeViewModel()
viewModel!.loadStatusCodes {
self.tableView.reloadData()
}
}
//This would do !
func loadStatusCodes(completion: #escaping () -> Void) {
apiClient.execute(service: .statusCode) { statusCodes in
self.statusCodes = statusCodes
completion()
}
}
// And in ViewController:
override func viewDidLoad() {
super.viewDidLoad()
viewModel = StatusCodeViewModel()
viewModel?.loadStatusCodes() {
self.tableView.reloadData()
}
}

Swift 4 delegates and passing textfield data

I am trying to make an app using the GoogleBooks API where I can use either a title or author or both to search for books. I am currently just working on the delegate portion to be able to pass the search terms to the results table view. However, I am getting errors with the variables I am using being let constants but I have them declared as var so I'm not sure where I am messing up.
This is the UIViewController code for the view with the two search boxes and the button:
import UIKit
protocol ViewControllerDelegate: class {
func searchInput(_ titleFromSearch: String?, authorFromSearch: String?)
}
class ViewController: UIViewController {
#IBOutlet weak var titleFromSearch: UITextField!
#IBOutlet weak var authorFromSearch: UITextField!
weak var delegate: ViewControllerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
titleFromSearch.delegate = self
authorFromSearch.delegate = self
}
override func touchesEnded(_ touches: Set<UITouch>, with event:
UIEvent?) {
super.touchesEnded(touches, with: event)
titleFromSearch.resignFirstResponder()
authorFromSearch.resignFirstResponder()
}
}
extension ViewController: UITextFieldDelegate {
func fieldsDidEndEditing(_ titleEntered: UITextField, authorEntered:
UITextField) {
if let delegateController = delegate {
delegateController.searchInput(titleFromSearch.text,
authorFromSearch: authorFromSearch.text)
}
}
}
And this is the code for the TableViewController that I have set up for the results to be displayed in.
import UIKit
import GoogleBooksApiClient
class SearchResultsTableViewController: UITableViewController {
var titleFromSearch: String?
var authorFromSearch: String?
var data = [Volume]()
override func viewDidLoad() {
super.viewDidLoad()
let session = URLSession.shared
let client = GoogleBooksApiClient(session: session)
let req = GoogleBooksApi.VolumeRequest.List(query: "Google")
let task: URLSessionDataTask = client.invoke(
req,
onSuccess: { [weak self] volumes in
self?.data = volumes.items
self?.tableView.reloadData()
},
onError: { error in
print("\(error)") }
)
task.resume()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection
section: Int) -> Int {
return data.count
}
override func tableView(_ tableView: UITableView, cellForRowAt
indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell",
for: indexPath)
let item = data[indexPath.row]
cell.textLabel?.text = item.volumeInfo.title
cell.detailTextLabel?.text = item.volumeInfo.authors.first
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
if let destination = segue.destination as? ViewController {
destination.delegate = self
}
}
}
Here is where I get the let constant error is with these two assignment statements:
extension SearchResultsTableViewController: ViewControllerDelegate {
func searchInput(_ titleFromSearch: String?, authorFromSearch: String?)
{
titleFromSearch = titleFromSearch
authorFromSearch = authorFromSearch
}
}
I added all the code because as I said I am new to iOS and I'm not sure where this error stems from in the code.
In the two lines causing your errors:
titleFromSearch = titleFromSearch
authorFromSearch = authorFromSearch
you are attempting to assign the parameters to themselves. You want to set the parameter values to your properties of the same name. To do this, add self. to the property references:
self.titleFromSearch = titleFromSearch
self.authorFromSearch = authorFromSearch

NSTableView Delegate methods won't get called

I'm currently trying to parse the reddit headlines from a specific subreddit and display these in an NSTableView. The thing is, the numberOfRows function gets called and returns the correct integer but the tableView delegate function never gets called.
As far as I can see everything is wired up correctly in the code.
ViewController:
#IBOutlet weak var tableView: NSTableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
Downloader.load(url: URL(string: "https://www.reddit.com/r/" + "gaming" + ".json")!){
(result) in
let tvc = TableViewController(data: result)
self.tableView.delegate = tvc
self.tableView.dataSource = tvc
self.tableView.reloadData()
}
}
TableViewController:
class TableViewController: NSObject{
var json: JSON!
init(data: JSON) {
super.init()
self.json = data
}
}
extension TableViewController : NSTableViewDataSource {
func numberOfRows(in tableView: NSTableView) -> Int {
return JSONFormatController.getTitlesFrom(json: json).count
}
}
extension TableViewController : NSTableViewDelegate {
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
var titles = JSONFormatController.getTitlesFrom(json: json)
if let cell = tableView.make(withIdentifier: "entry", owner: nil) as? NSTableCellView {
cell.textField?.stringValue = titles[row]
return cell
} else {
return nil
}
}
}
The result variable and getTitlesFrom method do work, I checked these.
I think your issue is that your TableViewController object is getting deallocated because you are not keeping a reference to it. Try this:
#IBOutlet weak var tableView: NSTableView!
var tvc : TableViewController!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
Downloader.load(url: URL(string: "https://www.reddit.com/r/" + "gaming" + ".json")!){
(result) in
self.tvc = TableViewController(data: result)
self.tableView.delegate = self.tvc
self.tableView.dataSource = self.tvc
self.tableView.reloadData()
}
}
Explanation: tvc is a local variable of the download block which is getting deallocated after it has executed. Presumably your assumption is that storing the tvc in delegate and/or dataSource is keeping tvc alive. But they are not, they are weak references.

NSWorkspace: runningApplications is not returning all user processes unless I use an NSTimer

I am trying to use NSWorkspace to get all the currently running user applications/processes. When I run the code below, it only returns a subset of all the currently running user applications and displays them in the tableView. Yet, when I uncomment the timer line, all the applications are returned and displayed in the table. I'm not sure why the first call to NSWorkspace.runningApplications doesn't immediately fetch all the currently running user applications? Thank you!
import Cocoa
class ViewController: NSViewController {
// MARK: - Properties
#IBOutlet weak var tableView: NSTableView!
var runningApplications = [NSRunningApplication]()
// MARK: - View Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
//NSTimer.scheduledTimerWithTimeInterval(2, target: self, selector: #selector(ViewController.refresh(_:)), userInfo: nil, repeats: true)
runningApplications = NSWorkspace.sharedWorkspace().runningApplications
print(runningApplications)
}
override var representedObject: AnyObject? {
didSet {
// Update the view, if already loaded.
}
}
func refresh(timer: NSTimer){
runningApplications = NSWorkspace.sharedWorkspace().runningApplications
tableView.reloadData()
}
}
// MARK: - NSTableViewDataSource
extension ViewController: NSTableViewDataSource {
func numberOfRowsInTableView(tableView: NSTableView) -> Int {
return runningApplications.count
}
}
// MARK: - NSTableViewDelegate
extension ViewController: NSTableViewDelegate {
func tableView(tableView: NSTableView, viewForTableColumn tableColumn: NSTableColumn?, row: Int) -> NSView? {
let cellView: NSTableCellView = tableView.makeViewWithIdentifier(tableColumn!.identifier, owner: self) as! NSTableCellView
cellView.textField?.stringValue = runningApplications[row].localizedName!
return cellView
}
}