Connecting NSTableView's delegate and dataSource to a custom subclass? - swift

My app reads text files into [Card]. Each Card has a two-letter code at the front, and I break up the cards into different NSTableViews depending on that code. That lets me set the layout in IB so each group of cards has an appropriate display.
I used to have all of the controller code in a single VC, but as the number of tables grew, so did the complexity of this code. Since the views differ primarily in layout and some default settings, they can all descend from a single class. So I did:
class CardView: NSTableView, NSTableViewDataSource, NSTableViewDelegate { ...
and, for one example...
class GeometryView: CardView { ...
Then I went to IB, selected the tableview, and changed it's class to GeometryView. Now I have to set up the delegate and dataSource, and this is where I have my problem : IB will not allow me to drag either setting to either the GeometryView or CardView.
So... do the targets of these IB settings have to be a particular subclass, say NSViewController? Or is there something I'm missing that lets IB see these as targets? I didn't do anything in the original VC, it just worked. Or am I simply doing the wrong thing in IB?
In this image you can see the tableview on the far left, the custom view subclass in the helper, and the connections for the tableview on the right. Any attempt to drag from the connections to anywhere in the helper fails. Note that the two existing connections are to the former delegate VC, which is what I am trying to replace.

I'm not sure why Interface Builder won't let you connect the delegate or data source to itself, but you could do it programatically. The awakeFromNib method is probably the best place for this, as it's called after both initWithFrame and initWithCoder:
override func awakeFromNib() {
delegate = self
dataSource = self
super.awakeFromNib()
}

Related

Swift difference between IBOutlet/IBAction and programatically constructing a view?

im kinda new to swift, and I don't understand what is the difference between the Interface Builder Outlet/Actions and programatically constructing a view. If I have IBOutlet why I don't need to still add them as subviews of the self.view and why do I don't need to instantiate them? As well, to not create two questions, why in the case of a creating a custom UiView I have to load the xib file with Bundle.loadNib?
Thanks,
You cannot initialize a property that is not nil by nature at instantiation time.
Interface Builder outlet, which always initializes after its owner. In this specific case — assuming it's properly configured in Interface Builder — you've guaranteed that the outlet is non-nil before you use it. That is why we can use as below:
#IBOutlet private var searchBar: UISearchBar!
Also, you already added subviews as well.
SearchViewController - ContentView - searchBar
That means ContentView or self.view is already superview of searchBar so you don`t need to add as a subview.
IBAction, IBOutlet, is responsible for connecting with objects on the Storyboard.
IBOutlet is a variable for accessing values, and IBAction can define an action on the event.
With Storyboard, prototypes can be created quickly and are easy to implement. However, if you work with multiple people, it can cause conflicts, so be careful.

Get Values from NSTextFields in Subclassed NSTableView

I have a reoccurring problem that I have not found a clean solution for. I have a NSTableView in NSViewController that uses a subclass of NSTableView to implement the datasource and delegate protocols e.g.
class SecondEdit: NSWindowController, NSWindowDelegate {
#IBOutlet weak var subTrackTable: NSTableView!
var sublist = SubTrackListing()
func loadTable() {
// other stuff left out for brevity
subTrackTable.setDataSource(sublist)
subTrackTable.setDelegate(sublist)
subTrackTable.reloadData()
}
in particular it should be noted that the XIB file for the WindowController contains the Table and the outlet for the table is connected to the NSWindowController class.
class SubTrackListing: NSTableView, NSTableViewDataSource, NSTableViewDelegate {
Usual NSTableView Delegate etc functions
}
This works like a charm and properly displays all of the data and keeps the required NSTableView functions in their own (smallish) class (desirable in as the Window / ViewController classes get pretty bloated by themselves).
My Problem is that I have NOT found a way to get user edits from the Table's texfields. IF the windowController class was also the NSTableView datasource / delegate, it would be trivial. I would just hook up the textfields IBActions and grab the sender stringValues. In the subclassed case, although you can hook up the IBActions just like before, THEY NEVER FIRE. I have tried setting the NSTableView subclass in IBs Custom Class dialog and putting the textfields in the SubTrackListing class, but not only does IB fight you on this every step of the way (you can't just drag from IB to the NSTableView subclass, you have to code the IBAction in the subclass first and then - sometimes - you can drag from the IBActions circle to the textfield in IB and get a recognized hook up), the IBAction - even when Xcode seems to think it's hooked up - NEVER FIRES (!). So I ask what the _____ !is going on here and how can you get NSTableView input from a subclassed NSTableView.
And yes I could just shove the NSTableView functions into the NSWindowController class, but not only would there be the what if you have more than one table situation, I have the general feeling that there Ought to be A Way to DO THIS in a subclass. . .
Any help would be appreciated. . .

How to get access to UITableViewController properties when subclassing from UIViewController?

I have subclassed UIViewController to provide common functionality for all UIViewControllers (for example I'm overriding viewDidLoad method). My app uses a bunch of view controllers that are arranged inside tab bar controller and in navigation controllers. Everything is OK, except the fact I have one UITableViewController. I would like to subclass not it but my custom MyUIViewController. I'm able to get the table working by implementing data source and delegate protocols:
#interface MaschinenTableViewController : MyUIViewController <UITableViewDataSource, UITableViewDelegate>
However in this case, I do not have an access to UITableViewController properties. For example, I cannot change the behavior of table row selection because self is MyUIViewController not UITableViewController:
self.clearsSelectionOnViewWillAppear = YES;
Are there any workarounds for accessing those properties?
In this case you will need to add a UITableView variable to the header and set it up appropriately in viewDidLoad, and add it to your view. From this point it will work as a UITableViewController will (as essentially that's all it does!)
Take a look at my article here which takes this approach.
You could also subclass UITableViewController as MyUITableViewController, implementing the behavior you want, and then put a MyUITableViewController as variable to your MyUIViewController.
Did the same as Simon Lee mentioned using the delegate.
But without storing the index anywhere, at the end of didSelectRowAtIndexPath method called the deselectRowAtIndexPath. Worked for me no issues so far.

Does UITableViewController allow the table to be in a UIView?

UITableViewController seems to always hijack the View link in IB. So, if I put UITableView in a UIView and link up the View to that UIView, it still doesn't work. Only the UITableView is shown.
What I'd like to do is use a UITableViewController and put some labels on top of the uiTableView that can be hidden.. Like loading.. and No results found.
The only solution I have come up with is to resort to using UIViewController and then adding a UITableView link to the class and link it up in IB.
Am I missing something here?
It's fine to use a UIViewController, make it implement the table view datasource and delegate protocols, and then hook a UITableView up to it. It's also fine to have the controller's main view be a container UIView, and have a UITableView as a subview of that.
And yes, this is probably the best way to add some kind of overlay view, such as a message label. So I think you're on the right track.
You should also be able to do this using a UITableViewController, instead of a UIViewController that explicitly implements the table view protocols. I've had success with this. I'm not sure what you mean when you say that UITableViewController "hijacks" the view outlet in IB.
It really isn't a big deal either way. UITableViewController doesn't do much other than implement those protocols, provide a different default loadView method, and call [tableView reloadData] by default on viewWillAppear:. If you do those things yourself, you'll be fine.

Is it possible to use something like an IBOutlet array?

I have a top list view in my current iPhone app, that's populated by code. I've assembled the view using Interface Builder, so it contains lot of UILabels.
Obviously I wouldn't like to have name1, name2, etc. outlets in my class, but prefer a name[10] array.
Is it possible to do so, and connect each item to the appropriate label (or any other interface builder like view)?
You can of course do this with interface builder, the keyword is IBOutletCollection. What it does is basically an NSArray out of multiple interface builder outlets.
IBOutletCollection(UILabel) NSArray *myLabels;
So the next thing would be connecting your labels in interface builder and then you can use the array to access all labels at runtime.
Follow these steps to create an array of outlets an connect it with IB Elements (Here is example of UIView, you can use UILabel also):
Create an array of IBOutlets
Add multiple UIElements (Views) in your Storyboard ViewController interface
Select ViewController (In storyboard) and open connection inspector
There is option 'Outlet Collections' in connection inspector (You will see an array of outlets there)
Connect if with your interface elements
-
class ViewController2: UIViewController {
#IBOutlet var collection:[UIView]!
override func viewDidLoad() {
super.viewDidLoad()
}
}
you cannot do it in IB, but you can create an array in your init method and add all your labels to it.
BTW, you can set some tag to each label and define macro to access it. smth like
#define NAME[TAG] (UILabel*)[self.view viewWithTag:TAG]
This can be done using outlet collections, see this related question.