1

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

bandejapaisa
  • 26,576
  • 13
  • 94
  • 112
  • I believe you need to use `self` inside a block, so `self.tableView` – Magnas Nov 12 '14 at 12:14
  • @Magnas That is correct if the variable is on the class, but tableView is also a parameter of the function. Either way, it doesn't work. – bandejapaisa Nov 12 '14 at 12:33
  • This feels like a bug. I just tried it with a very simple function with the same architecture in a playground, and it crashed xcode. – dustincarr Nov 12 '14 at 12:39

2 Answers2

0

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.

Mundi
  • 79,884
  • 17
  • 117
  • 140
  • The only reason for the struct is because I don't have static var's inside methods, like I would in Objective-C. I thought the reason would be clear from the code, but it's so I can find the cell height once. It does make sense here, because this is the only place it's used, so it's far nicer to keep it scoped to the function. – bandejapaisa Nov 12 '14 at 14:24
  • @mundi, this model is pretty much the standard for implementing a static variable in a function, typically for singletons. – David Berry Nov 12 '14 at 15:42
  • Maybe, though it crashes, so I am not sure... In any case, I am not convinced about this use case. I take issue with doing this in a `datasource` method. `UITableView` has a `rowHeight` property for this particular purpose. – Mundi Nov 12 '14 at 15:45
  • The compiler crash is caused by trying to reference a local variable from the enclosing scope, not by the basic model of putting a static in a locally defined structure. – David Berry Nov 12 '14 at 16:00
  • @Mundi the reason for this pattern is because I want my cell, which is defined in a nib file, to be the ONLY place I define the row height. I don't want it declared twice - this is more of an anti-pattern than doing what I'm doing in a datasource method. If I alter my nib file design... my UI updates and I don't have to worry about setting the rowHeight too. – bandejapaisa Nov 12 '14 at 16:28
0

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.

David Berry
  • 40,941
  • 12
  • 84
  • 95
  • And, as @mundi points out, the correct way would actually be to calculate it one time and assign it to rowHeight and not override the method at all. – David Berry Nov 12 '14 at 15:48
  • And why even define a struct if I only need the height?? – Mundi Nov 12 '14 at 15:49
  • That's the standard model for implementing a static variable in a function (typically as part of a singleton model implementation) Since Swift doesn't allow static variables to be defined at the function level, you instead define a structure that contains a static variable. The function initializer gives you the lazy, thread-safe initialization required for a singleton. For more discussion see [here](http://stackoverflow.com/questions/24024549/dispatch-once-singleton-model-in-swift) – David Berry Nov 12 '14 at 15:58
  • It is all so illogical. Why have a static variable in a method for dynamic row heights? A simple constant is absolutely sufficient in this use case. – Mundi Nov 12 '14 at 16:03
  • Well, I can see the desire to get it by pulling it out of the storyboard instead of a hard-coded constant that has to be coordinated between code and storyboard (or even two places on the storyboard, it's always annoyed me that I have to set the cell height two different places). I agree that the method used here is overly complex for this specific problem. – David Berry Nov 12 '14 at 16:06
  • Can I remind you that on my last line I stated 'I know there are other ways I can write this'... the point of the question was regarding the scope of a variable in the block, not on my pattern. – bandejapaisa Nov 12 '14 at 16:30
  • @Mundi TBH, I forgot about the rowHeight property on UITableView, which is why I was doing it in this datasource method. So I will change my implementation - but I'd still like to know about the scope of the variable in the block. – bandejapaisa Nov 12 '14 at 16:39
  • ..and if this were a superclass, and I overrode dequeueReusableCell to return a different cell, I'd likely override tableView:heightForRowAtIndexPath: too. (or set rowHeight to be a different height) – bandejapaisa Nov 12 '14 at 16:45