Call function in view controller from UITableViewCell - xcode

I have a problem which drives me nuts, how is it possible to invoke a function in a view controller from within a UITableViewCell. - I am doing that in Swift.
So I have a View Controller - called mainVC
in this mainVC I have declared a tableView which holds a UITableViewCell (Custom/ Dynamic) in that tableView cell I want call a function which is declared in the mainVC. If I do like self.superview?.myFunc doesn't work.
I hope someone can help me...

It is not as easy as that unfortunately.
What you want to look into is NSNotificationCenter or even better: - Delegates.
If you want a better understanding of Delegates I'd recommend this tutorial: http://www.raywenderlich.com/75289/swift-tutorial-part-3-tuples-protocols-delegates-table-views
If you don't want to read all that, I'll try my best to explain it here.
Let's say you have two view controllers: mainVC and otherVC.
Above the class definition in otherVC, add the following code:
protocol OtherVCDelegate {
func cellClicked()
}
Inside the class in otherVC, add the following property:
var delegate: OtherVCDelegate?
In the didSelectRow (or where you execute the code), add the following code:
self.delegate?.cellClicked()
Now, back in mainVC: Make mainVC conform to this delegate by adding it to the class like this:
class MainViewController: UIViewController, DetailDelegate {
}
In MainViewController add the delegate function and insite that one put your function you want to execute:
func cellClicked() {
println("Cell was clicked")
myFunc()
}
The last thing you have to do is make MainViewController the delegate of OtherViewController. This has to be done where you access the otherVC from the mainVC. Let's say it is done in prepareForSegue:
if segue.identifier == "showDetail" {
(segue.destinationViewController as DetailViewController).delegate = self
}
It is a little bit complicated at first, but once you get a grasp of it it's a walk in the park.

Related

Different classes work together

I work with swift 3 for macOS and I have a general question.
In my Storyboard are two View Controllers with a tableview for each View Controller.
Example:
View Controller A > VC_A.class
View Controller B > VC_B.class
Both View Controllers are elements of one Split View Controller.
now i would like to put one row element form VC A to VC B via drag and drop. this works fine, if both VC are in one class.
but now i would like to split it like the example below (VC_A and VC_B.class)
but how can i control the iboutlet tblview of VC_A in the VC_B.class?
You could use delegates and protocols for this. Setup a protocol class with your interface for editing tables eg
protocol EditableTableView {
func insertCell()
}
For both of your ViewControllers, set them to adhere to this protocol, implement the insertCell function and also add a delegate pointer.
class ViewControllerA : EditableTableView {
func insertCell() {
... add your code to insert a cell into VC A...
}
weak var otherTableViewDelegate : EditableTableView?
}
class ViewControllerB : EditableTableView {
func insertCell() {
... add your code to insert a cell into VC B...
}
weak var otherTableViewDelegate : EditableTableView?
}
In your parent split VC you can setup the delegate pointers so they point to the other view controller
viewControllerA.otherTableViewDelegate = viewControllerB
viewControllerB.otherTableViewDelegate = viewControllerA
Now whenever you want to insert a cell in the other controller you can call
self.otherTableViewDelegate?.insertCell()

How to add tableview datasource and delegate to a separate class/object?

I'm trying to learn how to make a native MacOS X app with SWIFT. I've no background in using Objective-C and even Xcode for the moment.
I achieved to do a simple app which answer to some button clicks and modify some labels. Now, I want to put some stuff in a table. I've put a NSTableView on my UI and figured out that I will have to implement a class which offer implementation for the NSTableViewDelegate and NSTableViewDataSource protocols.
I think that it would be cleaner, for code organization and readability, to put all the stuff to control my TableView in a separate module of the main AppDelegate. Anyway, any tutorial I've seen so far is just focused on how to implement the protocols and put them all in the AppDelegate class.
I've created a new SWIFT file and put the temporary following code inside it:
import Foundation
import Cocoa
class LogTableController: NSObject, NSTableViewDelegate, NSTableViewDataSource {
#IBOutlet var tableView: NSTableView!
var logTableData:[NSDictionary] = []
func numberOfRowsInTableView (tableView: NSTableView!) -> Int {
return 0
}
func tableView(tableView: NSTableView, objectValueForTableColumn tableColumn: NSTableColumn!, row rowIndex: Int) -> AnyObject! {
return "Hello"
}
}
But I'm unable to find how to proceed to make outlet beetween my TableView and my custom class or an instance of that class. Nothing allow me to do that in the InterfaceBuilder.
I think I'm missing something, any answer on how to organize my code to not put all the stuff in the AppDelegate class would be appreciated.
Thanks !
In Interface Builder you would need to add an object from the object library to the document outline and then set its class to your custom class. Then you can connect the delegate and datasource to that object.

Xcode6/Swift - How to implement an iAdBannerView in multiple view controllers?

Before I get started I am aware this question has been asked many times before, however all of them refer to xcode5/objective-C, not swift. I am only new to app development so I haven't been able to understand the objective-c and use it in swift.
I have got an adBannerView working on my first view controller, however how do I then take this banner and use it across my other 2 view controllers? Do I use the prepareForSegue function (and if so, how)?
My code for the adBannerView I currently have (from here)
//...
import iAd
class ViewController: UIViewController, ADBannerViewDelegate {
//link adBanner
#IBOutlet var adBannerView: ADBannerView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.canDisplayBannerAds = true
self.adBannerView.delegate = self
self.adBannerView.hidden = true
}
func bannerViewWillLoadAd(banner: ADBannerView!) {
NSLog("bannerViewWillLoadAd")
}
func bannerViewDidLoadAd(banner: ADBannerView!) {
NSLog("bannerViewDidLoadAd")
self.adBannerView.hidden = false
}
func bannerViewActionDidFinish(banner: ADBannerView!) {
NSLog("bannerViewDidLoadAd")
//optional resume paused game code
}
func bannerViewActionShouldBegin(banner: ADBannerView!, willLeaveApplication willLeave: Bool) -> Bool {
NSLog("bannerViewActionShouldBegin")
//optional pause game code
return true
}
func bannerView(banner: ADBannerView!, didFailToReceiveAdWithError error: NSError!) {
NSLog("bannerView")
}
//...
Thanks :)
When I have faced this problem my solution was to create a ParentViewController, and the others view controllers inheritate from him. In the parent view controller I create an outlet for a view which will contain iAdView, and in the .xib file of each view controller I link the iAdView container view to the created outlet. After that I create a singleton which has all the iAdView functionality and a property which is the iAdView. In the viewDidAppear of the parent I ask the singleton for the iAdView and add it as a subview of the iAd view container. Doing this that way in the view controllers you are not going to see any code of the iAdView, because all will be in the ParentViewController and in the singleton (lets call it iAdManager). Hope it helps.

Split View Controller: How to connect Master View Controller to Detail View Controller?

(Xcode6-beta3, Swift, iOS8, iPad)
In an iPad split-view controller, how do I link the Master View Controller to the Detail View Controller?
In other words, when the user taps on an item on the left, how do I change the view on the right?
I know that in didSelectRowAtIndexPath, I need to call a method... but how do I call a method in the Detail View Controller from the Master View Controller?
Example
Imagine an app to display information on different types of cheeses. We begin by dragging a split-view controller onto the storyboard. A table of items in the master view on the left is set up to read as follows.
Swiss
Cheddar
Brie
On the right, there is simply a Web View inside of the detail view controller, named cheeseViewController. Therein, HTML documents about the selected cheese will be displayed.
An IBOutlet is wired from the web view into cheeseViewController, and a method named 'changeCheese' is set up in the Detail View Controller delegate to swap out the document.
How can I make a tap on "Cheddar" change the information in the detail view?
EDIT: Do I have to modify my AppDelegate.swift file? Using a Master-Detail template, I tried the following, with no luck:
func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
// Override point for customization after application launch.
let splitViewController = self.window!.rootViewController as UISplitViewController
let navigationController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as UINavigationController
splitViewController.delegate = navigationController.topViewController as Paragraph
return true
}
I hope I understood your problem correctly: You would like to show the detail information of a selected cheese in your Detailview.
When you create a new Master-Detail-View application in XCode 6 Beta 3, there will be a variable called "detailItem" in your DetailViewController.Swift file:
var detailItem: AnyObject? {
didSet{
self.configureView()
}
You set this detailItem in your MasterViewController.Swift file in the following function:
override func prepareForSegue(segue: UIStoryBoardSegue, sender: AnyObject?){
if segue.identifier == "yourSegueIdentifier" {
let indexPath = self.tableView.indexPathForSelectedRow()
let cheeese = yourCheeseArrayWithDetailInformation[indexPath.row]
(segue.destinationViewController as DetailViewController).detailItem = cheeese
}
}
(Assuming, that you have linked the views with a segue with the identifier: "yourSegueIdentifier" and an array of detailinfo called "yourCheeseArrayWithDetailInformation")
The above mentioned function "configureView" in the DetailView can now access your detailItem, which contains the contents of "cheeese"
I hope this helps you.
Why don't you just post a Notification from didSelectRowAtIndexPath in your Master and add an observer in your Detail View most likely inside your viewDidLoad. You also can handle the selector within the observer method with closure.
If you didn't create a master-detail app (so you have no detailItem), you might use this:
if let
mySplitViewController = splitViewController,
detailView = mySplitViewController.childViewControllers.last as? DetailViewController {
// do something with it
}

Accessing controls of views of NSCollectionView

I'm using an NSCollectionView to display various objects. The whole things works rather well, except for one annoying thing. I cannot figure out how to access the various controls on the view used to represent each object in the collection.
Here's the setup:
I have dragged an NSCollectionView into my view in IB.
I made a custom subclass of NSCollectionViewItem. Mapped my class in IB.
I made a custom subclass of NSBox to act as the view for each object in the collection. Also mapped this class in IB and connected it to the view property of my NSCollectionViewItem subclass.
I made all the bindings in IB to display the correct information for each object.
The view:
The resulting collection view:
Reasoning that that my subclass of NSCollectionViewItem is basically a controller for each view in the collection, I made referencing outlets of the various controls in the view in my controller subclass:
#interface SourceCollectionViewItem : NSCollectionViewItem
#property (weak) IBOutlet NSTextField *nameTextField;
#property (weak) IBOutlet NSTextField *typeTextField;
#property (weak) IBOutlet RSLabelView *labelView;
#property (weak) IBOutlet NSButton *viewButton;
#end
When I inspect any instance of SourceCollectionViewItem in the debugger, all the properties show up as nil despite the fact that I can actually see them on my screen and that everything is displayed as it should be.
My setup was inspired by Apple's sample app IconCollection.
I am obviously missing something. What?
EDIT: I found various posts hinting at a similar issue:
CocoaBuilder.com and this question on SO.
EDIT: Just to be complete: this post deals with the subject as well and delivers a solution based on a combination of the options mentioned in the accepted answer.
Outlets are set during nib loading, and only the prototype item is loaded from nib and has its outlets assigned. All other ViewItems and their Views are cloned from the prototype, in that case outlets are just instance variables that are never initialized.
Here are the options I could come up with:
Override newItemForRepresentedObject: of collection view and reload nib instead of cloning the prototype. But this will probably hurt the performance greatly.
Override copyWithZone of collection view item and assign outlets manually using viewWithTag: to find them.
Give up and try to provide data via bindings only.
I found that overriding NSCollectionViewItem's -setRepresentedObject: could also be a good choice, as it is called on the new Item when all IBOutlet seem to be ready. After the call to super you can do whatever is needed:
- (void)setRepresentedObject:(id)representedObject
{
if (representedObject) {
[super setRepresentedObject:representedObject];
[self.anOutlet bind:#"property" toObject:self.representedObject withKeyPath:#"representeProperty" options:nil];
}
}
I used this method to bind a custom property of an interface object. The check is there to avoid useless calls, when the representedObject is not yet ready. The project uses a separate xib for the ViewItem, as explained in the links in the original edits.
Great question. Like #hamstergene suggests, you can use copyWithZone, it will be much more efficient compared to newItemForRepresentedObject. However viewWithTag is not always an option, first, because not everything can be tagged (easily), and, second, using tag for this purpose is a little wrong. Here's a cool approach with performance in mind, in Swift.
import AppKit
class MyViewController: NSCollectionItemView
{
// Here you are cloning the original item loaded from the storyboard, which has
// outlets available, but as you've seen the default implementation doesn't take
// care of them. Each view has a unique identifiers, which you can use to find it
// in sublayers. What's really cool about this, is that you don't need to assign
// any tags or do anything else while having advantage of better performance using
// cached nib object.
override func copyWithZone(zone: NSZone) -> AnyObject {
let copy: NSCollectionItemView = super.copyWithZone(zone) as! NSCollectionItemView
let oldView: RecordingView = self.view as! MyView
let newView: RecordingView = copy.view as! MyView
newView.foo = newView.viewWithIdentifier(oldView.foo.identifier!) as! NSTextfield
newView.bar = newView.viewWithIdentifier(oldView.bar.identifier!) as! NSImageView
return copy
}
}
#IBDesignable class MyView: View
{
// Custom collection view item view. Lets assume inside of it you have two subviews which you want
// to access in your code.
#IBOutlet weak var foo: NSTextfield!
#IBOutlet weak var bar: NSImageView!
}
extension NSView
{
// Similar to viewWithTag, finds views with the given identifier.
func viewWithIdentifier(identifier: String) -> NSView? {
for subview in self.subviews {
if subview.identifier == identifier {
return subview
} else if subview.subviews.count > 0, let subview: NSView = subview.viewWithIdentifier(identifier) {
return subview
}
}
return nil
}
}

Resources