how to retain view after addSubview of UIViewController with ARC - memory-management

How to handle situation when I use ARC and add view of UIViewController?
MyViewController *vc = [[MyViewController alloc] initWithNibName:#"MyViewController" bundle:nil];
[someView addSubview:vc.view]; //this retain vc.view
because addSubview retain onlu view, not controller, so controller is released. Before ARC there was a way to retain controller as long as neede, but how to prevent ARC to release View Controller?

I had similar situation solved by declaring vc as property with default strong attribute.

#define AntiARCRetain(...) void *retainedThing = (bridge_retained void *)__VA_ARGS; retainedThing = retainedThing
And then call AntiARCRetain(controller);

Why would you need a new ViewController there?
You should just add your View as Subview and handle everything with the ViewController of "someView"

Related

NSSplitViewController/NSSplitViewItem support in XIBs

Is there support for NSSplitViewController/NSSplitViewItem for XIBs? I see only NSSplitView
Can I just drag&drop NSViewController and subclass it as NSSplitViewController? How do I add NSSplitViewItem that it mostly works out of the box?
I can easily see support for them in storyboards.
The split view controller is not part of the object library for xib files. The easiest way to use split view controllers is to use storyboards.
If you are unwilling to use storyboards, your best option is to create a subclass of NSSplitViewController and select the checkbox to also create a xib file.
Add a split view to the split view controller xib file. Write code to load the xib file to set up the split view controller.
UPDATE
Look at the NSNib class reference for information on loading a xib file. The File's Owner of the xib file is your NSSplitViewController subclass. You may be able to use that information to set the split view controller. The worst case scenario is that you have to write code to load the split view from the xib file, set the split view controller's split view to the split view you loaded, and add the split view items to the split view controller. See the NSSplitViewController class reference for more information.
Yes it's possible. But it needs some wiring.
First add a custom subclass of NSSplitViewItem and expose viewController property as IBOutlet. Compiler will throw a warning so don't forget to mark property as dynamic.
#interface MySplitViewItem : NSSplitViewItem
#property IBOutlet NSViewController *viewController;
#end
#implementation MySplitViewItem
#dynamic viewController;
#end
In your XIB add 3 NSViewController objects. One of them change to custom class NSSplitViewController. It is important to note that one should NOT add NSSplitView. Wire NSViewControllers to it's views. Also add 2 objects and add custom class of MySplitViewItem which has exposed the viewController and wire it.
Last step. It is important to set property splitItems of NSSplitViewController before the views are loaded! Otherwise you are caught with NSAssert macro.
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSNib *nib = [[NSNib alloc] initWithNibNamed:#"Empty" bundle:nil];
NSMutableArray *test = [NSMutableArray new];
NSMutableArray *splitItems = [NSMutableArray new];
NSSplitViewController *controller;
[nib instantiateWithOwner:self topLevelObjects:&test];
for (id object in test) {
if ([object isKindOfClass:[NSSplitViewController class]]) {
controller = object;
}
if ([object isKindOfClass:[NSSplitViewItem class]]) {
[splitItems addObject:object];
}
}
[controller setValue:splitItems forKey:#"splitViewItems"];
[[self window] setContentViewController:controller];
}
Here is a proof that everything is wired correctly. Note that I did not touch delegate in XIB and it is wired. Magic, I know.
PS: XIB has to be set to prefer Coder + auto layout.
Why do I prefer XIB? Because we can create larger XIB which doesn't suffer from data isolation (Easily can do bindings across NSViewControllers).
I have also experimented to add splitViewItems in viewDidLoad or setView or awakeFromNib: in custom subclass of NSSplitViewController (with exposed NSSplitViewItem properties). If someone finds solution here it will be greatly appreciated.
Solution that requires code only:
- (NSSplitViewController *)profilesSVC
{
if (!_profilesSVC) {
NSSplitViewController *splitVC = [[NSSplitViewController alloc] init];
ProfilesViewController *profilesVC = [[ProfilesViewController alloc] initWithNibName:#"Profiles" bundle:nil];
NSSplitViewItem *leftItem = [NSSplitViewItem splitViewItemWithViewController:profilesVC];
[splitVC addSplitViewItem:leftItem];
ProfileViewController *profileVC = [[ProfileViewController alloc] initWithNibName:#"Profile" bundle:nil];
NSSplitViewItem *rightItem = [NSSplitViewItem splitViewItemWithViewController:profileVC];
[splitVC addSplitViewItem:rightItem];
_profilesSVC = splitVC;
}
return _profilesSVC;
}
I too wanted to add a splitView controller to my projet (macOS app) that doesn't use storyboards.
As it turned out, this was rather easy (in XCode 12.4).
As suggested, one has to to add NSViewController objects to the xib and wire each view property to the corresponding 'pane' (subview of the split view) in interface builder.
Then create a subclass of NSSplitViewController (no need to create a xib file).
Add a third NSViewController object to the xib and change its class to your subclass. Then wire both it's view and splitView properties to your splitView. It doesn't load any view if you just wire the splitView property.
Using a subclass of NSSplitViewController may not be required, but it's convenient as you may set the splitViewItems within viewDidLoad (below). Since this object is (automatically) the delegate of the splitView, you can also override delegate methods if you wish.
That object should have outlets leading to the NSViewController objects which you previously wired to the panes in IB.
I set two outlets named leftController and rightController.
My awakeFromNib method looks like this (sorry, I don't use swift):
- (void) viewDidLoad {
self.splitView.wantsLayer = YES; // I think this is required if you use a left sidebar with vibrancy (which I do below). Otherwise appkit complains and forces the use of CA layers anyway
NSSplitViewItem *left =[NSSplitViewItem sidebarWithViewController:leftController];
[self addSplitViewItem:left];
NSSplitViewItem *right =[NSSplitViewItem splitViewItemWithViewController:rightController];
right.minimumThickness = 420;
[self addSplitViewItem:right];
}
VoilĂ !
However, I get crashes if I set thick dividers in IB as appkit calls splitView:shouldHideDividerAtIndex too early, when there is apparently no divider yet. Worse, it may pass a negative divider index (!!). But you may override the method and act accordingly and I have no issue with thin dividers.

Change height of formSheet in iOS 8

I just have one question related to presenting a from sheet view controller in iOS 8. In iOS 7 I was able to change the height of the view controller using the last line of code in the function below:
SendRemainingEvaluationsViewController *sendRemainingEvaluationViewController = [[[SendRemainingEvaluationsViewController alloc] initWithNibName:#"SendRemainingEvaluationsViewController" bundle:nil]autorelease];
UINavigationController *navigationController = [[[UINavigationController alloc] initWithRootViewController:sendRemainingEvaluationViewController] autorelease];
navigationController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentViewController:navigationController animated:YES completion:nil];
//To Change the default size of the viewController which is presented in UIModalPresentationFormSheet presentation style
navigationController.view.superview.frame = CGRectMake(navigationController.view.superview.frame.origin.x, navigationController.view.superview.frame.origin.y, navigationController.view.superview.frame.size.width, 230);
But this does not work on iOS 8, not sure why? Does anyone knows what is the problem? any idea will be appreciated.
Thanks in advance.
After checking it, I could figure out the problem. After initializing the view navigation controller, its superview is nil and you can't access it right after presenting it. As a workaround, you can change the frame of its superview in the viewwillAppear of the view controller you're trying to present as you can see below:
- (void) viewWillAppear:(BOOL)animated
{
self.parentViewController.view.superview.frame = CGRectMake(self.parentViewController.view.superview.frame.origin.x, self.parentViewController.view.superview.frame.origin.y, self.parentViewController.view.superview.frame.size.width, 230);
}
This should work.

Adding ViewController to existing UINavigation stack

How to present modal ViewController on top of UINavigationController without using initWithRootViewController, just with adding it to the existing navigationcontroller stack?
my code is:
TableViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"TableView"];
UINavigationController *navi = [[UINavigationController alloc] initWithRootViewController:controller];
[self presentViewController:navi animated:NO completion:nil];
edit: what i actually want is to do like: "vc1 push vc2 modal vc3" and than use "poptoroot...to vc1". But the initWithRootViewController (vc3) is ruining it.
If you want to change the stack of navigationController. Use :
- (void)setViewControllers:(NSArray *)viewControllers animated:(BOOL)animated NS_AVAILABLE_IOS(3_0); // If animated is YES, then simulate a push or pop depending on whether the new top view controller was previously in the stack.
It will help.

how to add a uibuttonitem by calling a method from another class

Due to the neeeds of the aplication I need to call a method from a cell class of a collection view. The method is in a detail view controller which implementes the collection view .
If I call the method completely from viewDidLoad of the detail view controller at aplication start everything goes well and the button is added in the navigation bar as intended, but when I call the same method from the cell class nothing happens. Do I forget something?
Here is the code
collectionViewCell.m
- (void) addButtonToNavigationBar {
DetailViewController *dvc = [[DetailViewController alloc]init]
[dvc implementButton];
}
DetailViewController.h
- (void) implementButton;
DetailViewController.m
- (void) implementButton {
addButton = [[UIBarButtonItem alloc] initWithTitle:#"Done"
style:UIBarButtonItemStyleBordered
target:self
action:#selector(editTable)];
[self.navigationItem.rightBarButtonItem setStyle:UIBarButtonItemStyleBordered];
[self.navigationItem setRightBarButtonItem:addButton animated:YES];
Thank you for any hint.
When you write DetailViewController *dvc = [[DetailViewController alloc]init]; you are creating a new controller. It's not the one that already exists.
For your situation, you might consider having the controller listen for a NSNotification from the cell and call implementButton when it receives it.

Calling function from popover

Alright, so I made a popover from my main view and all that good stuff. But I want to have my popover call an action in my main view when a button within the popover is pressed.
MainView *mainView = [[MainView alloc] initWithNibName:#"MainView" bundle:nil];
[mainView doStuff];
The "dostuff" function changes some elements within the view. For example, the color of the toolbar is supposed to be changed. I've put a print command and the print command executes. But for some reason, the toolbar won't change color.
I've imported the header of MainView into the popover.
I did an #class thingy for MainView in my popover.
doStuff is declared in MainView's header.
The IBOutlets are declared too, and connected.
Any ideas?
Well its disappointing that we have no direct method that can be used to check in which view (view controller) the popover is shown. The thing that I am doing in tabbased application is:
New_iPadAppDelegate *appDel = (New_iPadAppDelegate *)[[UIApplication sharedApplication] delegate];
NSArray *viewConts = [(UINavigationController *)[[appDel tabBarController] selectedViewController] viewControllers];
MainViewController *viewController = (MainViewController *)[viewConts lastObject];
if([[viewController popoverController] isPopoverVisible]){
[viewController doStuff];
}
Hope this helps,
I know this is not the best way, hoping apple thinks about this issue, or if somebody has devised a work around.
Thanks,
Madhup

Resources