About "Declaration is only valid at file scope" - ios

I have a class and extension Swift file. After adding a delegate that I declared in another file to the class, Xcode shows this error
Declaration is only valid at file scope
at the extension line. I don't know what the problem is.
Can anyone help me to fix it?
class ListViewController: UIViewController, AddItemViewControllerDelegate {...}
extension ListViewController: UITableViewDataSource{
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
performSegueWithIdentifier("ShowDetail", sender: indexPath)
}
}

The error is somewhere in your ... — that error means that your ListViewController class didn't get closed, so the extension is being interpreted as nested inside, like this:
class ListViewController {
...
extension ListViewController {
}
}
Find the missing closing brace and you should solve the problem.

The extension must be at the root level - don't embed them into a class or whatever.

Make sure that the extension is declared at the end of your main class and after the last curly braces "}"
class ListViewController: UIViewController, AddItemViewControllerDelegate {
//Make sure that everything is clean here!
}
extension ListViewController: UITableViewDataSource{
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
performSegueWithIdentifier("ShowDetail", sender: indexPath)
}
}

Make sure your class and extension are seperated.
class ViewController: UIViewController {}
extension name: type {}

I had my extension calls at the bottom of my file and put them at the top and that fixed it for me. At the bottom, they were outside the class scope so I was a little stumped and just tried this.

The extension should be out of the Class.
class ListViewController: UIViewController, AddItemViewControllerDelegate {...}
// Code...
}
extension ListViewController: UITableViewDataSource{
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
performSegueWithIdentifier("ShowDetail", sender: indexPath)
}

Related

swift #IBAction from tableViewCell

I need to trigger a function by clicking on a tableViewCell,
until now I used #IBAction, but that option is only available with button type (I haven't found another way..)
this is the way I now of:
#IBAction func springPrs(_ sender: Any) {
//doing stuff..
}
but now I have an #IBOutlet
#IBOutlet weak var nextTrackCell: nextTableViewCell!
and I want to trigger a function by clicking on it. Any help?
This is a wrong approach, you should implement a delegate method from UITableViewDelegate called didSelectRowAt:
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//do your stuff here.
}
You shouldn't add an action directly to a table view cell because it violates the MVC design pattern and there is a handy callback already built into UITableViewDelegate to make this really easy.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.row == 0 {
// do something when the top row tapped
} else {
// do something when any other row is tapped
}
}
You could also declare a closure inside the cell, this is what I tend to use when having to pass some action to the view controller.
var onButtonPressed: (() -> ())?
#IBAction func buttonPressed(_ sender: Any) {
onButtonPressed?()
}
And use it like so in cellForRowAt:
cell.onButtonPressed = { [unowned self] in
// Do what you need to, no need to capture self however, if you won't access it.
}

Swift 3 Protocol and Delegate Method?

I've Looked around SO, and read a good number of blogs to find out how to accomplish my goal. The most understandable post I came across was the one here Pass data back to previous viewcontroller. I'm sure my understanding is mixed up but what I am trying to accomplish is removing an annotation from the map when I swipe a cell in my second view.
Removing the annotation from the CoreData is not a problem, removing the pin when I click the rightCallOut is not an issue either. The problem comes when I want to remove the annotation from the map in VC1 from an action in VC2. Where am I misunderstanding this simple process and how do I accomplish it?
FirstViewController
import UIKit
class ViewController: UIViewController, PinRemoverDelegate {
func removePin() {
mapView.removeAnnotation(selectedAnnotation)
}
}
SecondViewController
import UIKit
protocol PinRemoverDelegate: class {
func removePin()
}
class SecondViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
weak var delegate: PinRemoverDelegate? = nil
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let place = storedLocations[indexPath.row]
context.delete(place)
(UIApplication.shared.delegate as! AppDelegate).saveContext()
// Attempt To remove The Pin
delegate?.removePin()
}
tableView.reloadData()
}
}
The misunderstanding is only how to remove the pin. Calling a removePin function is wrong. Simply reload the CoreData, since the pin has already been removed from the CoreData.

Use of unresolved identifier in UITableViewDelegate extension

Instead of assigning the view controller as the UITableViewDelegate, I'm trying to reduce the code in the view controller by creating an extension for the UITableViewDelegate.
Why am I getting the error "Use of unresolved identifier companyDetailVC" for the line companyDetailsVC = CompanyDetailsViewController() when that is correct Swift 3 syntax?
Code
extension TableViewDelegate: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
companyDetailsVC = CompanyDetailsViewController()
self.present(companyDetailsVC, animated: true, completion: nil)
}
}
Edit: I'm trying to do this programmatically without storyboard. I created a UITableViewDelegate extension because I'm trying to reduce the code in the View Controller.
The code for presenting the Viewcontroller should be somewhat like this
and the extension should be like
extension YourClassNameHere: UITableViewDelegate {
//then your did select method comes here and in that put this code for presenting the viewcxontroller
let companyDetailsVC = CompanyDetailsViewController() //change this to your class name
self.present(companyDetailsVC, animated: true, completion: nil)
}
Replace your code with below code. You will need to provide your ViewController to create its extension and to use the property and method of it.
Make sure companyDetailsVC is declared in the controller. You don't need to use self until its called from block.
extension yourViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.companyDetailsVC = CompanyDetailsViewController()
self.present(companyDetailsVC, animated: true, completion: nil)
}
}

Swift: My custom UITableViewDataSource class thinks it doesn't conform to protocol 'UITableViewDataSource'

I have checked a few other questions out there, of which the answers seem to be in the datasource methods' declarations. However, I cannot find what's wrong with my methods declarations.
Here is my class:
import UIKit
class GuestControl: UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var leftTable: UITableView!
#IBOutlet weak var rightTable: UITableView!
var userList = [User]() //even numbers including 0 go on the left table, odd numbers go on the
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
//Here is where we link to that user's contact page
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: GuestTableViewCell = tableView.dequeueReusableCellWithIdentifier("guestCell") as! GuestTableViewCell
if tableView == leftTable {
cell.configureCellWithUser(userList[indexPath.row+indexPath.row])
} else {
cell.configureCellWithUser(userList[indexPath.row+indexPath.row+1])
}
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let num = Double(userList.count) / 2.0
if tableView == leftTable {
return Int(round(num)) //round up
} else {
return Int(num) //round down
}
}
}
And here is the exact error:
Protocol requires function 'tableView(_:numberOfRowsInSection:)' with type '(UITableView, numberOfRowsInSection: Int) -> Int'
Candidate is not '#objc', but protocol requires it
Protocol requires function 'tableView(_:cellForRowAtIndexPath:)' with type '(UITableView, cellForRowAtIndexPath: NSIndexPath) -> UITableViewCell'
Candidate is not '#objc', but protocol requires it
What is wrong?
As the error message indicates, the protocol requires that the table view data source methods are "Objective-C compatible". This can be achieved by
making your class inherit from NSObject (or any NSObject subclass)
class GuestControl: NSObject, ...
or adding the #objc attribute to the class definition
#objc class GuestControl: ...
or adding the #objc attribute to the data source methods
#objc func tableView(tableView: UITableView, numberOfRowsInSection section: Int) ...
In the first two cases, all methods of the class are made Objective-C
compatible.
If a (table) view controller is used as the table view data source then this automatically satisfied because UI(Table)ViewController inherits
from NSObject.

textDocumentProxy only applicable in KeyboardViewController.swift?

I am trying to create a custom keyboard extension that pushes to another UIViewController that has a list of text that can be inserted onto the selected text field. It subclasses a UIInputViewController. However, when I try to use the textDocumentProxy to input text on didSelectRowAtIndexPath() nothing seems to be happening.
Does the textDocumentProxy only apply to the default KeyboardViewController.swift file that was generated or am I able to to create another UIViewController that subclasses UIInputViewController to insert text? My last resort would be to use delegation or create a listener that will call a function in KeyboardViewController.swift, but I just wanted to see if anyone knew a better way.
Thanks in advance!
Code example is shown below:
import UIKit
class AnotherInputViewController: UIInputViewController, UITableViewDelegate, UITableViewDataSource {
...
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: false)
var proxy = textDocumentProxy as UITextDocumentProxy
if let input = arrayTextToInput[indexPath.row] as? String {
proxy.insertText(input)
}
}
}
Just in case any noobie runs into this again using the keyboard API extension...
I just injected the KeyboardViewController object into the view controller I was presenting as shown below:
//within KeyboardViewController.swift
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: false)
let nextVC: NextViewController = NextViewController(keyboardVC: self.keyboardVC)
self.presentViewController(nextVC, animated: true, completion: nil)
}
and then just initialized the nextVC with the KeyboardViewController
//class NextViewController
var keyboardVC: KeyboardViewController
init(keyboardVC: KeyboardViewController) {
self.keyboardVC = keyboardVC
super.init(nibName: nil, bundle: nil);
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
From there you have access to keyboardVC and can use that object to call methods to insert text into whichever fields you wish. For example...
call keyboardVC.addText(input) from wherever you want
//class KeyboardViewController.swift
func addText(input: String) {
if let textDocumentProxy = self.textDocumentProxy as? UITextDocumentProxy {
textDocumentProxy.insertText(input)
}
}
Hope it helps !
I faced the same problem, and I fixed it by using Delegation.
where pass action of insert text to KeyboardViewController
First create delegate
protocol UITextDocumentProxyDelegate: AnyObject {
func insertText(_ text: String)
}
, then in AnotherInputViewController create variable delegate with type UITextDocumentProxyDelegate
weak var delegate: UITextDocumentProxyDelegate?
and replace your code to
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: false)
if let input = arrayTextToInput[indexPath.row] as? String {
delegate?.insertText(input)
}
}
, and finally in KeyboardViewController conform UITextDocumentProxyDelegate protocol and implement insertText method
extension KeyboardViewController: UITextDocumentProxyDelegate {
func insertText(_ text: String) {
proxy.insertText(text)
}
}

Resources