ViewControllers and :(id) senders - uiviewcontroller

I want to make a class on my project for changing views. This is how I would usualy do it:
NewViewController * newView = [[NewViewController alloc] initWithNibName:#"NewViewController" bundle:NSBundle mainBundle]];
[self presentModalViewController:newView animated:NO];
I would like to send the NewViewController instance to another classes method so I can put the rest of the view changing code in there allowing for less clutter in my main class files. How would I go about doing this?
Would it be something like:
-(void) switchViews:(UIViewController *) switchTo{ }

if you make new viewController by modal, it allocates memory each other
In this case, Delegate is the best way you can do it easily.
"Delegate" means a protocol of classes. so have to practice it.

Related

What events are produced by UIControl on tvOS?

With UIButton, tvOS produces UIControlEvents.PrimaryActionTriggered (not .TouchUpInside, as it does on iOS) when the button gets focus and the user taps on the remote.
UIControl's don't seem to produce either of these events, however. I can't see that it produces any events, in fact, when the user focuses on the control and taps on the remote.
How does one use a custom UIControl for tvOS?
A UIControl doesn't emit any control events of its own accord. Your subclass is responsible for emitting events (generally by sending itself sendActionsForControlEvents:).
Since UIControl isn't currently documented for tvOS, it might be that Apple doesn't want you to subclass it.
Anyway, I've only played around with it a little, but apparently to implement your own UIControl subclass, you must override canBecomeFocused to return YES, and you must override pressesEnded:withEvent: to act on the user's press (presumably by sending yourself sendActionsForControlEvents:.).
You might also want to override pressesBegan:withEvent: to highlight your control.
Because UIControl conforms to the UIFocusEnvironment protocol (by way of UIView), you can override didUpdateFocusInContext:withAnimationCoordinator: to update your control's appearance depending on whether it has focus.
I spent the last couple weeks adding tvOS support for the widgets I package with my SVG rendering library SVGgh, so I have somewhat of a feeling for this. The issue is that there is what I think is a new or newish kind of action: UIControlEventPrimaryActionTriggered that gets sent out when the control wants to communicate a commitment of action, as in this response to the completion of a UIPress in my GHButton class. Remember to wire up a selector to this new action in your storyboard.
-(void) pressesEnded:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
{
[self sendActionsForControlEvents:UIControlEventPrimaryActionTriggered];
self.beingPressed = NO;
self.selected = self.selected;
}
But in a segmented control analog, the action isn't set on button push, but when the focused segment gets changed, as in the following (heavily edited for clarity) code from my GHSegmentedControl class:
- (BOOL)shouldUpdateFocusInContext:(UIFocusUpdateContext *)context
{
BOOL result = NO;
if(context.nextFocusedView == self)
{
result = YES;
[self highlight:YES];
if(context.focusHeading == UIFocusHeadingRight)
{
[self incrementValue];
[self.parentContent.control setNeedsFocusUpdate];
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
else if(context.focusHeading == UIFocusHeadingLeft)
{
[self decrementValue];
[self.parentContent.control setNeedsFocusUpdate];
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
}
return result;
}
Now that I think of it, it's possible I should be sending out a UIControlEventPrimaryActionTriggered action in the segmented control as well but UIControlEventValueChanged seems to work the way I expect.

IBDesignable UIViewController

I'd like to be able to layout my view controller in code but see the layout displayed in interface builder.
I know I can create a UIView subclass, make that IBDesignable, and assign it to the view controller's view, but this would require that I make all other subviews properties of this UIView subclass instead of properties of the view controller.
The real desire is to be able to layout my view controllers in code but quickly see any changes without rebuilding the project. If this is possible with playgrounds instead, an answer describing how to do that would also be appreciated.
Thanks for any suggestions.
I found a workaround to test view controller layout using IBDesignable.
1.Layout your view controller in code just as you'd do normally
2.Create an IBDesignable UIView subclass and add the view controller's view as a subview.
3.Create a xib and set the class of its view to the subclass you created in step 2
To elaborate on step 2, your IBDesignable's initWithFrame: method may look like
{
self = [super initWithFrame:frame];
MyViewController *viewController = [MyViewController alloc] init];
viewController.view.frame = self.bounds;
[self addSubview:viewController.view];
return self;
}
So beyond this initWithFrame method, all of your layout code can be handled by your view controller. You may want to pass data to your view controller in your subview's prepareForInterfaceBuilder method.
In order to layout you own classes in Xcode you need first to import then in your swift playground: here more information.
After you do that, it's came the "tricky" part. In order to make your how class debuggable and visibile in playground, your class must conform to the protocol: CustomPlaygroundQuickLooacable:
Here there is a quick example from the WWDC:
By implementing this protocol, you're basically telling playground how to represent you hown class. I haven't fond any better solution yet.

UITabbarController dismiss modal UINavigationController

I got a very interesting problem here. My iPhone app has an UITabbarController as rootViewController in the AppDelegate.
If the app is opened the first time, it must be configured basically. For this purpose I create an UINavigationController and tell the tabbarController to present it modally:
firstRun = [[firstRunViewController alloc] init];
navCtrl = [[UINavigationController alloc] initWithRootViewController:firstRun];
[[self tabBarController] presentModalViewController:navCtrl animated:NO];
When the configuration is done, I'd like to get rid of the firstRunViewController. I'm using this technique very often, using -dismissModalViewControllerAnimated:.
But in this constellation this doesn't work. It doesn't matter from what controller I'm calling the dismiss.
I tried it via the tabbarController, the rootViewController, the currently active viewController, of cause self and several other controllers.
EVERY TIME I call -dismissModalViewControllerAnimated: I get this exception:
'UIViewControllerHierarchyInconsistency', reason: 'presentedViewController for controller is itself on dismiss for: <UINavigationController:…
Can anybody help? Thanks in advance, with kind regards, Julian
EDIT
In my AppDelegate I'm using a UITabbarController as rootViewController for the main window:
self.window.rootViewController = self.tabBarController;
[self.window makeKeyAndVisible];
Then I'm creating an UINavigationController and tell the UITabbarController to present the modalViewController:
UINavigationController *navCtrl = [[UINavigationController alloc] initWithRootViewController:firstRun];
[[self tabBarController] presentModalViewController:navCtrl animated:NO];
When I now call -dismissModalViewControllerAnimated: on the firstViewController I'm getting the error from above.
In my opinion you are abusing UITabbarController. This class, even though a subclass of UIViewController, does not really use much of the UIViewController infrastructure.
What you want is a slight extension of what you have now. Create a new UIViewController subclass in your appDelegate, and add it as the single object to an array, and set the tabBar's viewControllers to this array. Set your subclass' hidesBottomBarWhenPushed to YES so it hides the tab bar when it becomes visible.
Now your app will launch and your UIViewController subclass will become the frontmost view. You can make this view the one you wanted to present modally, or you can present that view from your subclass using some kind of animation. Oh, and if you use the launch view as the background image for your subclass, you can really make this a smooth transition - I do this now.
When your modal view is done, then you can instantiate whatever views you want to then display, and set the UITabBarController to use those views with tabBarController.viewControllers (or the animated version). Poof, you UIViewController will get replaces (and under ARC just disappear).
I don't have a chance to test my hypothesis, but I suspect that this issue could depend on the fact that you are presenting the modal view too early, whereby too early means before the main window has had the chance to set up the tab bar controller. So, I would suggest this changes:
create a method to instantiate your navigation controller:
- (void)initializeAndPresentNavigationController {
UINavigationController *navCtrl = [[UINavigationController alloc] initWithRootViewController:firstRun];
[[self tabBarController] presentModalViewController:navCtrl animated:NO];
}
instead of presenting the navigation controller directly from appDidFinishLaunching, call the above method asynchronously:
[self performSelector:#selector(initializeAndPresentNavigationController) withObject:nil afterDelay:0.0];
Here the trick of calling the method as I do in 2 is that the call to initializeAndPresentNavigationController will be simply pushed on the main loop, and executed after your app has had the possibility to build its initial UI.
Hope it works for you.
I finally found the answer myself!
I just couldn't see the wood for the trees! I'm quite happy right now! :)
I did really silly things: In the last viewController of the setup viewControllers I had to change the tabars viewControllers corresponding to whether the user is administrator or not. So I did:
appDelegate.tabBarController.viewControllers = [NSArray arrayWithObjects:appDelegate.readState,
appDelegate.navCtrl,
appDelegate.settings, nil];
You can see that I was adding the AppDelegate's "navCtrl" to the tabbar's viewControllers. So I was trying to dismiss a viewController I just added to the parentViewControllers (UITabbarController) sub-controllers.
Dismissing something I want to present just in the same moment is NOT advisable! :))

Pass information to another viewcontroller without pushing or using segues

I'm trying to "bookmark" (sort of) an object from one UIViewController so that it then shows on a different UIViewController. Currently, the only way I can get this to work is if I either push to the UIViewController that will display the bookmarks or if I perform a segue which is basically the same thing.
Is there a different approach to this?
if you just want to send a variable (without loading the view). This is how I do it.
DetailViewController *detailvc = [[[DetailViewController alloc] init] autorelease];
detailvc.event = [self.fetchedResultsController objectAtIndexPath:indexPath];
And another type of segue I've been doing (without the need of the whole function) i just ask the storyboard to go...
[self.storyboard instantiateViewControllerWithIdentifier:#"detailViewController"];
Just don't forget to name the identifier in the sotyboard, properties, identifier.
Hope it helps...
I've been having some trouble re-learning everything with the storyboard as well.

UIViewController remove from superview after receiving nsnotification

i'm trying to remove a UIViewController from the superview after receiving a notification but it doesn't work.
in my appDelegate i do something like this:
- (void)applicationDidFinishLaunching:(UIApplication *)application{
NSLog(#"applicationDidFinishLaunching");
[window addSubview:tabBar.view];
splash = [[SplashViewController alloc] init]; <-- this i want to remove
[window addSubview:splash.view];
[window makeKeyAndVisible]; }
in the SplashViewController i register the view to receive NSNotifications like this:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(fadeScreen:) name:#"CloseSplashScreen" object:nil];
the SplashViewController appears right after the Default.png and should fade smoothly after some data is parsed...
in my TableViewControler i want to send the notification in the viewWillAppear method but it doesn't work.
- (void)viewWillAppear:(BOOL)animated{
NSLog(#"viewWillAppear");
[[NSNotificationCenter defaultCenter] postNotificationName:#"CloseSplashScreen" object:self];}
the method gets called but the view is not removed from the superview. i tried to put the postNotification in - (void)viewDidLoad but the method doesn't get called...
any ideas why this is not working? thank you in advance
Since it looks like you are trying to implement some sort of splash screen, consider using a UINavigationController to present the splash. What you are doing is adding a view controllers view as a subview of UIWindow yet no one is managing your view controller. This is not how you should be doing things.
UIWindow has the method setRootViewController which should be in your appDelegate. That way you can push your Splash Controller, and after a certain interval push the first viewController of the app.
As a sidenote, I understand that some developers use splash screens as a means of reinforcing brand identity. That being said, consider Apples Human Interface Guidelines:
As much as possible, avoid displaying a splash screen or other startup
experience. It’s best when users can begin using your app immediately.