Custom protocol is not working in swift - ios

I have two UIViewControllers HomeViewController and ArrangeViewController.
In ArrangeViewController, I have this code
import UIKit
protocol ArrangeClassProtocol
{
func recieveThearray(language : NSMutableArray)
}
class ArrangeViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { // class "ArrangeViewController" has no initializer
var ArrangeClassDelegateObject : ArrangeClassProtocol?
// Global Variables Goes Here
var languageNamesArray: NSMutableArray = ["Tamil","English"]
var userDefaults : NSUserDefaults = NSUserDefaults.standardUserDefaults()
var tempArray : NSMutableArray = NSMutableArray()
// Outlets Goes Here
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Saving the Array in a UserDefaultObject
if userDefaults.boolForKey("languageNamesArrayuserDefaults")
{
tempArray = userDefaults.objectForKey("languageNamesArrayuserDefaults") as NSMutableArray
}
else
{
tempArray = languageNamesArray
}
self.tableView.separatorInset = UIEdgeInsetsZero
// TableView Reordering
self.tableView.setEditing(true, animated: true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func prefersStatusBarHidden() -> Bool
{
return true
}
// Delegate Methods of the UITableView
func numberOfSectionsInTableView(tableView: UITableView!) -> Int {
return 1
}
func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
return tempArray.count
}
func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
let cell = tableView.dequeueReusableCellWithIdentifier("Arrange", forIndexPath: indexPath) as ArrangeTableViewCell
cell.languageName.font = UIFont(name: "Proxima Nova", size: 18)
cell.languageName.text = tempArray.objectAtIndex(indexPath.row) as NSString
cell.showsReorderControl = true
return cell
}
// Delegate Methods for dragging the cell
func tableView(tableView: UITableView!, editingStyleForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCellEditingStyle
{
return UITableViewCellEditingStyle.None
}
func tableView(tableView: UITableView!, canMoveRowAtIndexPath indexPath: NSIndexPath!) -> Bool
{
return true
}
func tableView(tableView: UITableView!, moveRowAtIndexPath sourceIndexPath: NSIndexPath!, toIndexPath destinationIndexPath: NSIndexPath!)
{
var stringToMove = tempArray.objectAtIndex(sourceIndexPath.row) as NSString
tempArray .removeObjectAtIndex(sourceIndexPath.row)
tempArray .insertObject(stringToMove, atIndex: destinationIndexPath.row)
}
func tableView(tableView: UITableView!, targetIndexPathForMoveFromRowAtIndexPath sourceIndexPath: NSIndexPath!, toProposedIndexPath proposedDestinationIndexPath: NSIndexPath!) -> NSIndexPath!
{
let section:AnyObject = tempArray .objectAtIndex(sourceIndexPath.section)
var sectionCount = tempArray.count as NSInteger
if sourceIndexPath.section != proposedDestinationIndexPath.section
{
var rowinSourceSection:NSInteger = (sourceIndexPath.section > proposedDestinationIndexPath.section) ? 0 : (sectionCount-1)
return NSIndexPath(forRow: rowinSourceSection, inSection: sourceIndexPath.row)
}
else if proposedDestinationIndexPath.row >= sectionCount
{
return NSIndexPath(forRow: (sectionCount-1), inSection: sourceIndexPath.row)
}
return proposedDestinationIndexPath
}
// Creating the HomeViewController Object and presenting the ViewController
#IBAction func closeButtonClicked(sender: UIButton)
{
userDefaults.setObject(tempArray, forKey: "languageNamesArrayuserDefaults")
userDefaults.synchronize()
ArrangeClassDelegateObject?.recieveThearray(languageNamesArray)
self.dismissViewControllerAnimated(true, completion: nil)
}
}
In the HomeViewController, I this code.
class HomeViewController: UIViewController, ArrangeClassProtocol {
var ArrangeClassObject : ArrangeViewController = ArrangeViewController() // ArrangeViewController is Constructible with ()
override func viewDidLoad() {
super.viewDidLoad()
self.ArrangeClassObject.ArrangeClassDelegateObject = self
}
func recieveThearray(language: NSMutableArray)
{
println(language)
}
}
I wanted to access the Array that am passing from the ArrangeViewController. But its showing errors that I commented out near to the statements. I also used the optional values with the HomeViewController, It also showing error and crashes the app. Please somebody help to figure this out.
I got this idea by a github post. In that project he used one UIViewController and one another swift class. That is also possible for me. But i want to work it out with these two UIViewControllers.

Thanks for the new code. The problem that's creating your error message is here:
#IBOutlet weak var tableView: UITableView!
If you change this code to:
#IBOutlet weak var tableView: UITableView?
Then the compiler will stop complaining about the lack of an initialiser.
However, that's more a diagnostic tool than an actual fix for the problem, which is that you need an initialiser. I think what's going on is that up until you add an uninitialised property at your own class's level in the hierarchy, you're being provided with a default initialiser. At the point you add your uninitialised property, Swift will stop generating the default initialiser, and expect you to provide your own.
If you check the header for UIViewController, you'll find this advice:
/*
The designated initializer. If you subclass UIViewController, you must call the super implementation of this
method, even if you aren't using a NIB. (As a convenience, the default init method will do this for you,
and specify nil for both of this methods arguments.) In the specified NIB, the File's Owner proxy should
have its class set to your view controller subclass, with the view outlet connected to the main view. If you
invoke this method with a nil nib name, then this class' -loadView method will attempt to load a NIB whose
name is the same as your view controller's class. If no such NIB in fact exists then you must either call
-setView: before -view is invoked, or override the -loadView method to set up your views programatically.
*/
init(nibName nibNameOrNil: String!, bundle nibBundleOrNil: NSBundle!)
So, perhaps just try:
init() {
super.init(nibName: nil, bundle: nil)
}
...which should restore the functionality of the originally-provided initialiser. I'm guessing your IBOutlet will be properly "warmed up" from the Storyboard by the base class's initialiser, and you should again be able to construct the View Controller with an argumentless initialiser.

Related

cellForRowAtIndexPath and numberOfRowsInSection conflicting in tableView

I am creating an app that is retrieving data from Firebase. In my 'MealViewController' I have a TableView that has the view controller as it's delegate and data source. I am getting the issue "Type 'MealViewController" does not conform to protocol 'UITableViewDataSource' because it requires both :numberOfRowsInSection: and :cellForRowAtIndexPath: . However, when I add both, another issue appears - 'Definition conflict with previous value'. I've looked through all the Stack Overflow issues related to this, and no luck has been had. Here's my View Controller:
class MealViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var bgImage: UIImageView?
var image : UIImage = UIImage(named: "pizza")!
#IBOutlet weak var blurEffect: UIVisualEffectView!
#IBOutlet weak var mealTableView: UITableView!
var items = [MealItem]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
bgImage = UIImageView(image: image)
bgImage?.contentMode = .ScaleAspectFill
bgImage!.frame = view.layer.bounds
self.view.addSubview(bgImage!)
//self.bgImage?.addSubview(blurEffect)
//bgImage!.bringSubviewToFront(blurEffect)
view.bringSubviewToFront(blurEffect)
mealTableView.layer.cornerRadius = 5.0
mealTableView.layer.borderColor = UIColor.whiteColor().CGColor
mealTableView.layer.borderWidth = 0.5
let ref = Firebase(url: "https://order-template.firebaseio.com/grocery-items")
mealTableView.delegate = self
mealTableView.dataSource = self
// MARK: UIViewController Lifecycle
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(items.count)
return items.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> MealsCellTableViewCell { //issue occurs here
let groceryItem = items[indexPath.row]
if let cell = mealTableView.dequeueReusableCellWithIdentifier("ItemCell") as? MealsCellTableViewCell {
cell.configureCell(groceryItem)
// Determine whether the cell is checked
self.mealTableView.reloadData()
return cell
}
}
func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
// [1] Call the queryOrderedByChild function to return a reference that queries by the "completed" property
ref.observeEventType(.Value, withBlock: { snapshot in
var newItems = [MealItem]()
for item in snapshot.children {
let mealItem = MealItem(snapshot: item as! FDataSnapshot)
newItems.append(mealItem)
}
self.items = newItems
self.mealTableView.reloadData()
})
}
func viewDidDisappear(animated: Bool) {
super.viewDidDisappear(animated)
}
func willAnimateRotationToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) {
}
}
override func willAnimateRotationToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) {
bgImage = UIImageView(image: image)
bgImage?.contentMode = .ScaleAspectFill
bgImage!.frame = view.layer.bounds
self.view.addSubview(bgImage!)
view.bringSubviewToFront(blurEffect)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: UITableView Delegate methods
}
The cellForRowAtIndexPath should look like this:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "ItemCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! MealsCellTableViewCell
let groceryItem = self.items[indexPath.row]
cell.configureCell(groceryItem)
return cell
}
Note that the returned cell is a MealsCellTableViewCell which is a subclass of UITableViewCell so it conforms to that class.
Don't change the function definition as that will make it not conform to what the delegate protocol specifies.
Here's a link to the Apple documentation for the specific implementation of custom tableView cells for reference.
Create a Table View
The problem is that your view controller's conformance to UITableViewDatasource cellForRowAtIndexPath method is not right. You should refactor your implementation of cellForRowAtIndexPath method like so:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let groceryItem = items[indexPath.row]
guard let cell = tableView.dequeueReusableCellWithIdentifier("ItemCell") as? MealsCellTableViewCell else {
fatalError("No cell with identifier: ItemCell")
}
cell.configureCell(groceryItem)
return cell
}
You also need to move the datasource methods out of viewDidLoad method.
You return MealsCellTableViewCell instead of UITableViewCell in cellForRowAtIndexPath method, that's the reason.

delegate returns nil tvOS

I am trying to figure out from long time. Can someone tell me why my delegate method is never called. Its a tvOS project but i believe it should work as simple iOS app. On click of button i have a popup table view and on select i am trying to update button label with selected option.
protocol PopupSelectionHandlerProtocol{
func UpdateSelected(data:String)
}
class PopupViewController: UIViewController, UITableViewDataSource,UITableViewDelegate {
#IBOutlet weak var myTable: UITableView!
let months = [1,2,3,4,5,6,7,8,9,10,11,12]
let days = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31]
let yearsRange = [2015,2016,2017,2018,2019,2020]
var popupType:String!
var delegate:PopupSelectionHandlerProtocol?
override func viewDidLoad() {
super.viewDidLoad()
popupType = "months"
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (popupType == "months"){
return 12
}else if (popupType == "days"){
return 31
}else if (popupType == "years")
{
return 6
}
return 10
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
cell.textLabel?.text = String(months[indexPath.row])
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print(tableView.cellForRowAtIndexPath(indexPath)?.textLabel?.text)
delegate?.UpdateSelected((tableView.cellForRowAtIndexPath(indexPath)?.textLabel?.text)!)
self.dismissViewControllerAnimated(true, completion: nil)
}
}
And then This -
class VacationPlannerController: UIViewController,PopupSelectionHandlerProtocol {
#IBOutlet weak var fromMonth: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
let popupDelegate = PopupViewController()
popupDelegate.delegate = self
}
func UpdateSelected(data:String){
print("Inside UpdateSelected VacationPlannerController \(data)")
fromMonth.titleLabel?.text = data
}
}
The problem is that, you are getting your delegate as nil, since there can be only one ViewController at a time presented. Since your popupViewController's view is not loaded. The viewDidLoad() method is not getting called, resulting in non-setting of popupDelegate.
If you want to check its nullity. Try this in your didSelect... Method
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.navigationController?.pushViewController(VacationPlannerController(), animated: true)
if(delegate==nil){
print("delegate is nil")
}
delegate?.UpdateSelected((tableView.cellForRowAtIndexPath(indexPath)?.textLabel?.text)!)
}
If you want the fromMonth button to be updated. First you will need to present/push VacationPlannerController in order to call its viewDidLoad(). Then only you will be able to update its property, that is, fromMonth label.
Two things to resolve this issue -
First in PopupViewController-
In didSelectRowAtIndexPath, replaced
delegate?.UpdateSelected((tableView.cellForRowAtIndexPath(indexPath)?.textLabel?.text)!)
with
self.delegate?.UpdateSelected((tableView.cellForRowAtIndexPath(indexPath)?.textLabel?.text)!)
And Second in VacationPlannerController-
Removed below code from viewDidLoad -
let popupDelegate = PopupViewController()
popupDelegate.delegate = self
And added prepareForSegue -
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let destinationVC = segue.destinationViewController as! PopupViewController
destinationVC.delegate = self
}
And issue resolved yeeee :)

swift - Method does not override any method from its superclass & 'UITableView' does not have a member named 'viewDidLoad'

I am very new to swift and when I say new, I mean I just started this morning, I am having an issue I tried googling for but can't seem to find a solution.
I have this swift file:
import UIKit
class ProntoController: UITableView {
var tableView:UITableView?
var items = NSMutableArray()
override func viewDidLoad() {
super.viewDidLoad()
}
func viewWillAppear(animated: Bool) {
NSLog("Here")
let url = "API.php"
NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: url)!) { data, response, error in
NSLog("Success")
}.resume()
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items.count;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("CELL") as? UITableViewCell
return cell!
}
}
My problem is with this section of code:
override func viewDidLoad() {
super.viewDidLoad()
}
I get these two errors:
'UITableView' does not have a member named 'viewDidLoad'
Method does not override any method from its superclass
You need to subclass UITableViewController, you're subclassing UITableView

Troubleshooting Custom UITableView Cell [Swift]

I have created a custom UITableView cell in my Storyboard that looks like this:
I hooked it up to my UITableViewCell class like this:
import UIKit
class StatusCell: UITableViewCell {
#IBOutlet weak var InstrumentImage: UIImageView!
#IBOutlet weak var InstrumentType: UILabel!
#IBOutlet weak var InstrumentValue: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
Finally I attempted to initialize the UITableView from my UIViewController as such:
import UIKit
class SecondViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var TableView: UITableView!
let Items = ["Altitude","Distance","Groundspeed"]
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.Items.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell: StatusCell! = tableView.dequeueReusableCellWithIdentifier("Cell") as StatusCell!
cell.InstrumentType?.text = Items[indexPath.row]
cell.InstrumentValue?.text = "150 Km"
cell.InstrumentImage?.image = UIImage(named: Items[indexPath.row])
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
However, when I attempt to run the program I get an EXC_BAD_INSTRUCTION error:
What could I have done wrong? Any help would be appreciated!
The debugger output shows that cell is nil, meaning it could not be instantiated. Furthermore, you are forcing an unwrapping of the optional (using the !) which causes the app to crash on the nil value.
Try to change your cellForRowAtIndexPath method like so (notice the dequeueReusableCellWithIdentifier method):
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as StatusCell
cell.InstrumentType.text = Items[indexPath.row]
cell.InstrumentValue.text = "150 Km"
cell.InstrumentImage.image = UIImage(named: Items[indexPath.row])
return cell
}
Assuming that your custom tableViewCell class is correctly set up and the outlets are bound, there is no need to check for optionals.
Place a breakpoint on the let cell = ... line and step through the code. Check if cell gets initialized and is not nil.
And please: Do not use upper case names for properties and variables (your outlets, Items array...) as upper case names are for classes, structs, ...

Strong reference cycle: closures vs methods

I have a project that contains a UITableViewController called TableViewController. Because I want my UITableViewDataSource protocol declaration to be outside of my TableViewController declaration, I've set the following code (inspired by Objc.io Lighter View Controllers):
TableViewController:
class TableViewController: UITableViewController {
let array = [["1"], ["2", "3", "4"], ["5", "6"]]
var dataSource: DataSource!
override func viewDidLoad() {
super.viewDidLoad()
dataSource = DataSource(array: array, configureCellBlock: { (cell, item) in
cell.textLabel.text = item
})
tableView.dataSource = dataSource
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
deinit {
println("Quit TVC")
}
}
DataSource:
class DataSource: NSObject, UITableViewDataSource {
let array: [[String]]
typealias TableViewCellConfigureBlock = (cell: UITableViewCell, item: String) -> ()
var configureCellBlock: TableViewCellConfigureBlock
init(array: [[String]], configureCellBlock: TableViewCellConfigureBlock) {
self.array = array
self.configureCellBlock = configureCellBlock
super.init()
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return array.count
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return array[section].count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
let data = array[indexPath.section][indexPath.row]
configureCellBlock(cell: cell, item: data)
return cell
}
deinit {
println("Quit DataSource")
}
}
This works fine. But now, I want to replace the configureCellBlock closure with a method. So I've changed my TableViewController code to this:
class TableViewController: UITableViewController {
let array = [["1"], ["2", "3", "4"], ["5", "6"]]
var dataSource: DataSource!
override func viewDidLoad() {
super.viewDidLoad()
dataSource = DataSource(array: array, configureCellBlock: formatCell)
tableView.dataSource = dataSource
}
func formatCell(cell: UITableViewCell, item: String) -> () {
cell.textLabel.text = item
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
deinit {
println("Quit TVC")
}
}
The problem now is obvious: if I run this code, TableViewController and DataSource are never deallocated because of a strong reference cycle.
I've been trying to change my dataSource declaration to weak var dataSource: DataSource! or unowned var dataSource: DataSource but none of my recent tries worked.
How can I replace my configureCellBlock closure with a method? Do I have to use a protocol delegate pattern to do so? What would it look like?
The problem is that the reference to formatCell has an implied reference to self. This is not resolved by making the data source weak (you definitely want a strong reference there), but rather making sure that the block variable in the data source does not maintain a strong reference back to the view controller. So, you'd add [unowned self] to the start of the closure:
dataSource = DataSource(array: array) {
[unowned self] cell, item in
self.formatCell(cell, item: item)
return
}
You can implement it with a delegate like this:
#objc protocol TableViewCellConfigurator {
func dataSource( dataSource: DataSource, configureCell cell: UITableViewCell, item: String)
}
class DataSource: NSObject, UITableViewDataSource {
weak var cellConfigurator: TableViewCellConfigurator?
(...)
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
let data = array[indexPath.section][indexPath.row]
if let delegate = cellConfigurator {
cellConfigurator.dataSource( self, configureCell: cell, item: data)
}
return cell
}
(...)
}
class TableViewController: UITableViewController: TableViewCellConfigurator {
override func viewDidLoad() {
super.viewDidLoad()
dataSource = DataSource(array: array, configureCellBlock: formatCell)
tableView.dataSource = dataSource
dataSource.cellConfigurator = self
}
override func dataSource( dataSource: DataSource, configureCell cell: UITableViewCell, item: String) {
cell.textLabel.text = item
}
}

Resources