Control a Nstabviewcontroller from Parent View

How do I update elements of the parent view/viewController from a ContainerView?

Getting the parent view controller in the container view and casting it to the type of your parent controller (as mentioned in
thefredelement's comment) will work, however it has some drawbacks.

  1. Your container view should really be a view that just happens to be
    a MVC itself. As a view, it should not know who is controlling it,
    It might however know that it has a delegate that it can send
    messages to. For example does a UITextView know that it is a child
    of a particular class of view controller? No, but it knows it might
    have a delegate that it can send messages to.
  2. You can't reuse this view controller as a child of any other view controller since it is tightly coupled to the parent class
  3. By simply casting the received parent to type of your parent you are bypassing the type checking.

So the "quick fix" might be to do as suggested in the comment, however, I am providing this answer as a suggestion as how I think in the long run this should be handled. Hopefully others find it useful. I know for me going through the thought process was helpful.

Ideally I think you should declare a variable in the ContainerViewController that either is the Class of your parent view controller, or even better a protocol that your parent controller will conform to. Then in the prepare for segue of the parent view controller you would set this variable.

At the same time your parent should (or at least can) have a variable declared as being of the type of your container view or another protocol which the container will conform to. Then also in the prepare for segue of the parent, get a handle for the Container view controller and store it in the variable you declared.

So something like this:

In your ContainerViewController

var myContainerViewDelegate: ParentViewControllerOrProtocol

In your parent view controller

weak var myChildViewOutlet:  ChildViewControllerOrProtocol

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "embededSegueToChild" {
if let dvc = segue.destinationViewController.contentViewController as? ChildViewControllerOrProtocol {
dvc.myContainerViewDelegate = self
myChildViewOutlet = dvc
}
}

This way your ContainerViewController looks like view object (which I think is what it is supposed consider to be), and the myChildViewOutlet variable is your outlet to that view (which probably should be weak).

The myContainerViewDelegate reference in the Container essentially sets up your parent as the delegate for the container view controller.

now both view controllers have a handle for each other

Swift/Cocoa: How to bind a value to currently selected tab

You should be able to use an NSTabViewController and its selectedTabViewItemIndex property, which is specifically documented to be bindings-compliant.

If you need to, you can create a computed property that's built on top of selectedTabViewItemIndex to map to an appropriate model object for the selected item. When you do that, be sure to implement the class method keyPathsForValuesAffecting<Key> to return ["selectedTabViewItemIndex"] so that KVO knows to consider your computed property as changed whenever selectedTabViewItemIndex changes.

(This is probably not sufficient to make the computed property you already tried work, because NSTabView's selectedTabViewItem itself is not documented as being KVO-compliant.)

Swift macOS SegmentedControl Action not getting called

SOLUTION

let parentViewControllerInstance = self.parent as! ParentViewController
segmentedControl.target = parentViewControllerInstance

In my case I just had to set the delegate as the target of the sendAction method.


Background

Ok, after hours of reading the AppKit Documentation I am now able to answer my own question.

First, debugging the UI showed that the problem was definitely not in the ViewHierarchy.Debugging UI

So I tried to think about the nature of NSButton and NSSegmentedControl. At some point I noticed that both are subclasses of NSControl.

class NSSegmentedControl : NSControl
class NSButton : NSControl

The AppKit Documentation says:

Discussion

Buttons are a standard control used to initiate actions within your app. You can configure buttons with many different visual styles, but the behavior is the same. When clicked, a button calls the action method of its associated target object. (...) You use the action method to perform your app-specific tasks.

The bold text points to the key of the solution – of its associated target object. Typically I define the action of an control item like this:

button.action = #selector(someFunc(_:))

This causes the NSControl instance to call this:

func sendAction(_ action: Selector?, to target: Any?) -> Bool

Parameter Description from the documentation:

Parameters

theAction


The selector to invoke on the target. If the selector is NULL, no message is sent.


theTarget


The target object to receive the message. If the object is nil, the application searches the responder chain for an object capable of handling the message. For more information on dispatching actions, see the class description for NSActionCell.

In conclusion the NSControl instance, which was firing the action method (in my case the NSSegmentedControl), had no target to send its action to. So it was only able to send its action method across the responder chain - which obviously has been nil while the first responder was located in another view.

Connecting drill-down NSSplitView outlets using Storyboards

The new NSSplitViewController and NSTabViewController class are what Apple is calling container controllers. you can get references to other controllers within the container using the new property's in NSViewController

@property (readonly) NSViewController *parentViewController
@property (copy) NSArray *childViewControllers

so for example if you would like to get a reference to your child Table controller from your parent table controller. In your parent table controller viewDidLoad you can do the following

myChildTableController = [self.parentViewController childViewControllers][1];

or vice versa from your child table controller

myParentTableController = [self.parentViewController childViewControllers][0];

How to exclude certain AppKit views from restorable NSWindow?

The key to state restoration is the NSResponder method restoreStateWithCoder:

This method is part of the window restoration system and is called at launch time to restore the visual state of your responder object. The default implementation does nothing but specific subclasses (such as NSView and NSWindow) override it and save important state information. Therefore, if you override this method, you should always call super at some point in your implementation.

https://developer.apple.com/documentation/appkit/nsresponder/1526253-restorestate

So, to not restore a certain control, make this method a no-op.

It says that you "should always call super", but that restores the window state. So if you don't want window restoration, don't call super.

In the case of a Tab View, it evidently must be done in the NSTabView (subclass) itself. In other views, overriding this method on the View Controller may work.

class SomeTabView: NSTabView {

override func restoreState(with coder: NSCoder) {
// Do NOT restore state
}

}

Implement Empty View for NSTabView

OK, so that's how I've managed to fix it.

It turns out this "Empty View" implementation, apart from print a rounded box with a label in it, in the very middle of the parent view, failed to re-draw the main background. So, all it takes is to repaint it...

In drawRect: just add :

[[NSColor grayColor] set]; // or any other color you prefer
NSRectFill([self bounds]);


Related Topics



Leave a reply



Submit