Assigning UITableView delegate and dataSource to super in Swift - ios

I am trying to have a super class fill and handle the contents of a table for a certain segment. So I thought of implementing:
#IBAction func changeValue(sender:AnyObject){
self.searchDisplayController?.setActive(false, animated:false)
if (selection.selectedSegmentIndex==1){
self.myTableView.delegate=super
self.myTableView.dataSource=super
} else {
self.myTableView.delegate=self
self.myTableView.dataSource=self
}
self.myTableView.reloadData()
}
Yet I have an error. By way of testing, the compiler suggested me to use:
#IBAction func changeValue(sender:AnyObject){
self.searchDisplayController?.setActive(false, animated:false)
if (selection.selectedSegmentIndex==1){
self.myTableView.delegate=super.self()
self.myTableView.dataSource=super.self()
} else {
self.myTableView.delegate=self
self.myTableView.dataSource=self
}
self.myTableView.reloadData()
}
whatever the meaning of construct super.self()
Yet, notwithstanding the code passes through there without any problem, the command seems to be ignored and the delegate methods are called on the same class instead of the super one and even printing the value of super.self() shows it is the current class, notwitstanding, when I skip the (), Xcode encourges me by saying:
Function produces expected type 'LogsViewController'; did you mean to call it with '()'?
Yet when I add the double parenthesis it returns the current class instead of the top LogsViewController.
What is the correct way of implementing what I need, and why super.self() does not work as it is advertised?
I clarify I cannot simply call the delegate methods on super as that shall retain the ownership to the bottom class and virtual methods on a top class shall find the methods in the bottom class instead of the one of the super one as it would be needed.
That its the structure of my code, hoping it makes things clearer:
Bottom Class
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
let isMainTable = tableView==self.myTableView
let isFavorite = selection.selectedSegmentIndex==2
var count: Int?
count = sourceForTableKind(isMainTable, favorites:isFavorite)?.count
if (count==nil) {
return 0
}
return count!
}
Top "virtual class":
func sourceArray()->Array<NearElement>?{
return nil
}
func arrayOfContentsFromSearchArray(searchArray:Array<String>?, favorites:Bool)->Array<NearElement>?{
return nil
}
func sourceForTableKind(normal: Bool, favorites:Bool)->Array<NearElement>?{
// NSLog(#"source:%# %#",favorites?#"favorites": #"near", normal?#"normal":#"searched");
print("sono \(self)")
if (normal) {
return sourceArray()
} else {
return arrayOfContentsFromSearchArray(searchResults, favorites:favorites)
}
}
The implementations of sourceArray() and arrayOfContentsFromSearchArray() are defined both in the bottom class and its super class both inheriting from this class.

Simply because when you are assigning delegate you are assigning instances.
So if the delegate method is called it will call the object's method which is an instance of "self" class.
When you assign it to super it keeps referencing to your instance and it doesn't matter if you used "self" or "super"(this is to confirm).
What you should do, in your class implementation of the delegate method call the super's implementation.

you have to put:
myTableView.delegate = self
myTableView.dataSource = self
When you assign it to super it keeps referencing to your object and it doesn't matter if you used "self" or "super".

A solution for this case would be:
Superclass implements tableview's delegate and datasource methods.
A separate class (called for example TableDelegate) which implements tableview's delegate and datasource methods.
if (selection.selectedSegmentIndex == 1) {
self.myTableView.delegate = self;
self.myTableView.dataSource = self;
} else {
self.myTableView.delegate = instanceOfTableDelegate;
self.myTableView.dataSource = instanceOfTableDelegate;
}`
When superclass delegate, datasource are needed to be called, assign self, Swift will find the implementation from the superclass, otherwise assign to the class which is used only as delegate and datasource for the tableview.

I ended up, as also suggested, by calling the super on all delegate methods. Of course it would have been cleaner to switch the table or its delegate and dataSource to the top class, but that simply does not work in Swift.
Yet, even this pushes the virtual function to call the bottom implementation. Calling the top delegate methods does not apparently change the active instance the virtual function finds when implementing its methods. Of course I would need to awkwardly send upwards even the implementations, but this is definitely a bug of the support.

Related

How to call the same function on multiple classes?

I have an UIViewController with 4 UIButtons. A user can tap any of those UIButtons and an UIView pops up. I want to add an didAppear() and didDisappear() function on the classes which are holding the UIViews depending on the users action. How can I call didDisappear() without the use of an enum, for example:
func didDisappear(view: EnumViews){
switch view{
case view0: myClassWithView0.didDisappear()
case view1: myClassWithView1.didDisappear()
case view2: myClassWithView2.didDisappear()
case view3: myClassWithView3.didDisappear()
}
}
Now I get 4 times duplicate data. I know that function exists for my class with a UIView, but how to call it? I made a protocol:
protocol ViewProtocol{
func didAppear()
func didDisappear()
}
I made the classes which are holding the UIView's conform to that protocol. However I do not know how to use it, when I create the class I get the error:
'myClassWithUIView' cannot be constructed because it has no accessible
initializers
The classes are all in an array and I can identify which UIView needs to pop up from the sender.tag. Ideally, I want to have something like this:
#IBAction func bringNewUIView(_ sender: UIButton) {
let newView = myArrayOfClassesWithUIView[sender.tag]
newView.didAppear()
}
You've got many things going on here. I'll start with the easy one.
'myClassWithUIView' cannot be constructed because it has no accessible initializers
This just means you don't have an initializer for your class. So inside your myClassWithUIView implementation you need to have init. I can't really help you with building the init because I don't know how that class is structured, but I will assume this is something you know how to do anyway.
Your #IBAction seems fine. Once you have an array of your classes that seems like it should work. Edit your post if that is not the case.
Finally, for your didDisappear question, you can do something like this:
func didDisappear(view: EnumViews) {
//Check to see if this view conforms to your ViewProtocol (that's not a good name, btw)
if let myClass = view as? ViewProtocol {
//Since it does conform to ViewProtocol you can call didDisappear on it
myClass.didDisappear()
}
}
Alternatively, if you already know that the didDisappear function is always passing in a view that conforms to ViewProtocol why not just change the argument and make that easier?
func didDisappear(view: ViewProtocol) {
view.didDisappear()
}

Delegate not working

I have a protocol declared in a class
public protocol demoDelegate {
func willShowdemoResult(DemoGraph: UIView)
}
Now I am calling this in the same class where the protocol is declared.
public class Demo:UIViewController {
public var delegate : demoDelegate!
//some code
self.delegate.willShowdemoResult(self.demoGraph())
}
where demo graph returns a UI graph
func demoGraph() -> UIView {
//some code
return demoGraphView
}
I am getting an error that unexpectedly found nil while wrapping an optional value. I know the reason that I have not initialised the delegate. Can somebody guide me How to initialise the delegate here.
The function is being called in other class
class DemoResult: UIViewController, demoDelegate{
func willShowdemoResult(DemoGraph: UIView)
// some code
}
Please Help
You are getting the error, because Demo.delegate is nil when calling:
delegate.willShowdemoResult(self.demoGraph())
Before you make this call, make sure, that you have set the delegate property. I would recommend this right after initializing Demo or right after DemoResult got the address of the Demo-instance.
Let's assume, you have stored an instance of Demo in DemoResult.demoVC. Then you can set the delegate in DemoResult like this:
demoVC.delegate = self
BTW: It's better to use optional types to store delegates:
public var delegate: demoDelegate?
When delegate is optional, delegate?.willShowdemoResult(self.demoGraph()) won't crash, if delegate has not been initialized yet.

iOS Swift - Accessing method in Child from Parent

In my app I have a Storyboard with a bunch of elements laid out. I am setting properties of these elements from "ViewController.swift".
On the storyboard are two UIViews, which have been subclassed to allow for drawing methods. They are to be used as "signature" fields, where a user can sign their signature into the box. This is working fine.
One of the subclassed methohds of the UIViews is "eraseBitmap" which clears the UIView of the drawing.
class SigView: UIView {
...
func eraseBitmap() {
path = nil
cache = nil
}
}
I have a button that calls a function "erase" in the parent VC.
I have tried doing
func erase() {
SigView.eraseBitmap()
}
However that generates an error saying that I'm missing an argument. eraseBitmap, however, accepts no arguments.
If I add an argument, regardless what it is, I get a "SigView not convertible to..." error.
Any ideas, or a better way of coding this part?
Your SigView class defines a method eraseBitmap() - something like:
class SigView : UIView {
func eraseBitmap () { /* ... */ }
}
You'll apply this method to instances of SigView. So, somewhere you've got an instance of SigView in your UI, like:
var aView : SigView = ...
You'll then use:
func erase () {
aView.eraseBitmap()
}
or perhaps
func erase (view:SigView) {
view.eraseBitmap()
}
The error you are getting is caused by attempting to invoke a non-class method on a class. Non-class methods can only be invoked on instances of classes.

Delegate Error in Swift

I am trying to send a double value from a UIView (which is loaded from a XIB) to a ViewController using a delegate
Here is my protocol, it is just sending a double to the main ViewController on a button click.
protocol HoursWorkedDelegate{
func sendHoursWorked(hoursWorked: Double);
}
class HoursWorkedView: UIView{
var delegate: HoursWorkedDelegate?;
#IBAction func calculateHoursWorked(sender: AnyObject){
// Do some calculations for the hoursWorked value
// Give value
delegate!.sendHoursWorked(hoursWorked);
}
}
// This class now wants that Double value
class ViewController: UIViewController, HoursWorkedDelegate{
// Conform to protocol
func sendHoursWorked(hoursWorked: Double){
// Lets say we just want to assign this value to a textField
hoursWorkedTextField.text = NSString(format: "%.4f", hoursWorked);
}
}
The error message I get is Thread 1: EXC_BAD_INSTRUCTION(code = EXC_I386_INVOP, subcode=0x0)
Any help would be much appreciated, Thank You!
As a start, change the exclamation point in this snippet to a question mark:
delegate!.sendHoursWorked(hoursWorked);
This is what's likely causing the crash, as you are force-unwrapping the optional delegate property. A question mark means we'll only call sendHoursWorked() on the delegate if the delegate exists.
That fix will now probably mean that your program is no longer crashing, but you still don't get the desired results, because sendHoursWorked() is never called. We have to tell our HoursWorkedView object who is delegating it.
Somewhere in your code, you might have something like this:
let hoursWorkedView = HoursWorkedView()
self.view.addSubview(hoursWorkedView)
It's right here where we should be setting the delegate:
let hoursWorkedView = HoursWorkedView()
hoursWorkedView.delegate = self
self.view.addSubview(hoursWorkedView)
Though if it's me, I probably add a constructor to HoursWorkedView that accepts the delegate property:
init(delegate: HoursWorkedDelegate) {
super.init()
self.delegate = delegate
}
And now we can just do this:
let hoursWorkedView = HoursWorkedView(delegate: self)
self.view.addSubview(hoursWorkedView)
I think you're getting your view and your viewcontroller mixed up: a ViewController controls things; a view just displays them. The viewController tells the view what to display.
So, you want to connect your button to the viewController -- not the view. And you don't need a custom view class or a delegate.
Set it up like this:
create a textField and a button
create an outlet for the textField
put calculateHoursWorked directly in your viewController
create an action to connect the button to calculateHoursWorked
in calculateHoursWorked, set self.textField.text to the result of the calculation (where "textField" is whatever you named your outlet)
You wouldn't use a delegate in this context because the viewController knows everything the view does. The delegate pattern is for cases where one object has no visibility into another.
EDIT:
That being said, the bug here is that the delegate isn't actually being set anywhere.
Swift Optionals (the ! and ?) help prevent cases like this. If you explicitly unwrap an optional using !, you have to make sure it's always defined. In this case, since delegate is defined as optional (?) you have to check it:
#IBAction func calculateHoursWorked(sender: AnyObject){
// Do some calculations for the hoursWorked value
// Give value
if let currentDelegate = self.delegate {
currentDelegate.sendHoursWorked(hoursWorked)
}
}

Scope of a variable inside a block, in a struct, in a func

I want to do this:
public func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
struct Cell {
static let height: CGFloat = {
var cell:RentalViewCell = tableView.dequeueReusableCellWithIdentifier("RentalViewCell") as RentalViewCell
return cell.bounds.size.height
}()
}
return Cell.height
}
.. but the swift compiler throws a wobbly, and gives me a segmentation error in creating the SIL because of the tableView variable inside the block.
I know there are other ways I can write this, but can someone explain why it doesn't work this way and why I can't access tableView variable. I've tried using a capture list in the block to no avail.
Thanks
It does not make any sense to define a struct in a table view datasource / delegate method.
Define the struct elsewhere and - if you really have a good reason for it -
use it in the datasource method.
I suspect what you really want is to set the table view's rowHeight property.
Think about what your code is saying, you want a static instance, height, to be initialized over all invocations of tableView(heightForRowAtIndexPath:) Does it really make sense then, for that static instance to be initialized with the value of a single call? Imagine that this were a super class and that subclasses used different implementations of dequeueReusable..., which implementation should be used to determine the height for ALL subclasses?
A far better implementation would be to calculate it one time into an optional on the UITableViewController subclass and cache it there.

Resources