presentModalViewController with ARC - objective-c

I found this snippet of code to use for good transitions:
http://srooltheknife.blogspot.co.uk/2012/03/custom-transition-animation-for-modal.html
The problem is that it uses 'modal' and my program uses ARC so modal has been deprecated in iOS 6.0.
It's only two lines that use modal, so I changed them from-
TransitionController.m:
[self dismissModalViewControllerAnimated:NO];
to:
[self dismissViewControllerAnimated:NO completion:Nil];
and
[self presentModalViewController:modalViewController animated:NO];
to:
[self presentViewController:modalViewController animated:NO completion:nil];
and I execute it with:
FirstPage.m:
#implementation FirstPage
{
TransitionController *tran;
}
- (IBAction)StartGame:(id)sender
{
if (! self.mainPage)
self.mainPage = [[MainGameDisplay alloc] initWithNibName:nil bundle:nil];
[tran presentModalViewController:self.mainPage withPushDirection:kCATransitionFromBottom];
}
StartGame is a button. The program runs perfectly with no errors, but when I click the button nothing happens.
UPDATE:
FirstPage.h:
#import <UIKit/UIKit.h>
#import "MainGameDisplay.h"
#import <QuartzCore/QuartzCore.h>
#import "TransitionController.h"
#interface ViewController : Engine
{ }
- (IBAction)StartGame:(id)sender; //Action connection ID.
#property(nonatomic, readwrite) MainGameDisplay *mainPage;
FirstPage.m:
#implementation ViewController
{
TransitionController *tran;
}
- (IBAction)StartGame:(id)sender {
if (! self.mainPage)
self.mainPage = [[MainGameDisplay alloc] initWithNibName:nil bundle:nil];
tran = [[TransitionController alloc] initWithNibName:Nil bundle:Nil];
[tran presentModalViewController: self.mainPage withPushDirection:kCATransitionFromBottom];
}
When the 'StartGame' button is clicked, the current view is pushed to
the left by the exact same view.
There is a slight fade/glow of the view during the transition.
The direction used in the method is different to the direction of the transition. E.g 'kCATransitionFromBottom' would actually be the view being pushed from the left.
Output with every button click: "Warning: Attempt to present on whose view is not in the window hierarchy!"

Related

Objective-C: Tabbaritem tapped->Method Called-> But WebView not refreshed

Trying to achieve
When I tap on the tabbaritem say #2, it will called the method and reload the web view.
Issue
When I tap on the tabbaritem, the method is called but web view did not reload.
Did not load the web view
Question
If I called the method on the VC itself. I can manage to reload the web view. Only if I called it when the tabbaritem is tapped, it doesn't reload the web view.
Code
MyTabBarController.m
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
NSLog(#"controller class: %#", NSStringFromClass([viewController class]));
NSLog(#"controller title: %#", viewController.title);
if (viewController == [tabBarController.viewControllers objectAtIndex:2])
{
[(UINavigationController *)viewController popToRootViewControllerAnimated:YES];
tabBarController.delegate = self;
[[[Classes alloc] init] LoadClasses];
}else if (viewController == [tabBarController.viewControllers objectAtIndex:3]){
[(UINavigationController *)viewController popToRootViewControllerAnimated:YES];
tabBarController.moreNavigationController.delegate = self;
[[[Gym alloc] init] handleRefreshGym:nil];
}else{
[(UINavigationController *)viewController popToRootViewControllerAnimated:NO];
}
}
Classes.m
- (void)LoadClasses {
sURL = #"www.share-fitness.com/apps/class.asp?memCode=SF100012&dtpClass=13/09/2018&lang=EN&lat=37.785835&long=-122.406418&ver=1&plat=IOS"
NSLog(#"The URL To be loaded %#", sURL);
NSURL *url = [NSURL URLWithString:sURL];
sRefresh = sURL;
[[NSURLCache sharedURLCache] removeAllCachedResponses];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
[webView loadRequest:urlRequest];
[webView setDelegate:(id<UIWebViewDelegate>)self];
UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
[refreshControl addTarget:self action:#selector(handleRefresh:) forControlEvents:UIControlEventValueChanged];
[webView.scrollView addSubview:refreshControl];
}
As I mentioned in my other reply Objective-C: How to properly set didSelectViewController method for TabBarController, so I can refresh the VC everytime it is tapped, I don't think it's good User Experience to be refreshing the view from the server every time the tab bar is selected (this will get very annoying for users to wait every time for the server to refresh the data)
That being said, the issue with the code you posted is that you're initializing a new instance of your classes in the TabBarControllerDelegate method so the method will be called on this new instance instead of on the one that's displaying/exists in your TabBarController's view controllers. Specifically these two lines are initializing the new instances:
[[[Classes alloc] init] LoadClasses];
[[[Gym alloc] init] handleRefreshGym:nil];
Instead you should be finding the instance that already exists, and calling the method on them.
I would recommend creating a ParentViewController with a public method along the lines of - (void)doStuffWhenTabBarControllerSelects; (just example naming to be clear what's it doing to you) then have each of the view controllers you'd like to have do something when they're selected be child classes of this parent (and have their own implementation of - (void)doStuffWhenTabBarControllerSelects;). This way in the TabBarController's delegate method, you can just find the appropriate instance of ParentViewController (associated with the view controller being selected) and call the - (void)doStuffWhenTabBarControllerSelects; method on it.
Here's an example of what I mean:
ParentViewController.h:
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
#interface ParentViewController : UIViewController
- (void)doStuffWhenTabBarControllerSelects;
#end
NS_ASSUME_NONNULL_END
ParentViewController.m:
#import "ParentViewController.h"
#interface ParentViewController ()
#end
#implementation ParentViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)doStuffWhenTabBarControllerSelects {
NSLog(#"Fallback implementation if this method isn't implemented by the child class");
}
#end
FirstViewController.h:
#import <UIKit/UIKit.h>
#import "ParentViewController.h"
#interface FirstViewController : ParentViewController
#end
FirstViewController.m:
#import "FirstViewController.h"
#interface FirstViewController ()
#end
#implementation FirstViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)doStuffWhenTabBarControllerSelects {
NSLog(#"I'm doing stuff on the %# when the tab bar controller delegate calls back to selection", NSStringFromClass([self class]));
}
#end
SecondViewController.h:
#import <UIKit/UIKit.h>
#import "ParentViewController.h"
#interface SecondViewController : ParentViewController
#end
SecondViewController.m:
#import "SecondViewController.h"
#interface SecondViewController ()
#end
#implementation SecondViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)doStuffWhenTabBarControllerSelects {
NSLog(#"I'm doing stuff on the %# when the tab bar controller delegate calls back to selection", NSStringFromClass([self class]));
}
#end
MyTabBarController.h:
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
#interface MyTabBarController : UITabBarController <UITabBarControllerDelegate>
#end
NS_ASSUME_NONNULL_END
MyTabBarController.m:
#import "MyTabBarController.h"
#import "ParentViewController.h"
#implementation MyTabBarController
- (void)viewDidLoad {
[super viewDidLoad];
self.delegate = self;
}
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
// since your view controllers are embedded in nav controllers, let's make sure we're getting a nav controller
if ([viewController isKindOfClass:[UINavigationController class]]) {
// we're expecting a nav controller so cast it to a nav here
UINavigationController *navController = (UINavigationController *)viewController;
// now grab the first view controller from that nav controller
UIViewController *firstViewControllerInNav = navController.viewControllers.firstObject;
// check to make sure it's what we're expecting (ParentViewController)
if ([firstViewControllerInNav isKindOfClass:[ParentViewController class]]) {
// cast it to our parent view controller class
ParentViewController *viewControllerToCallMethodOnAfterSelection = (ParentViewController *)firstViewControllerInNav;
[viewControllerToCallMethodOnAfterSelection doStuffWhenTabBarControllerSelects];
}
}
}
#end
Then when you select between the two tabs you'll this is the output:
I'm doing stuff on the FirstViewController when the tab bar controller delegate calls back to selection
I'm doing stuff on the SecondViewController when the tab bar controller delegate calls back to selection
I'd recommend doing some additional research/reading of the documentation:
There's a good amount of beginner information here: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/DefiningClasses/DefiningClasses.html#//apple_ref/doc/uid/TP40011210-CH3-SW1
UITabBarController: https://developer.apple.com/documentation/uikit/uitabbarcontroller?language=objc
UITabBarControllerDelegate:
https://developer.apple.com/documentation/uikit/uitabbarcontrollerdelegate?language=objc
One other helpful hint is that within Xcode you can hold down on the option key and click on something to show a quicklook into the explanation/documentation
You can also right click on something and "Jump To Definition". The majority of Apple's implementations will will have additional information in the header.
Here's the example of what's in the header of UITabBarController:
/*!
UITabBarController manages a button bar and transition view, for an application with multiple top-level modes.
To use in your application, add its view to the view hierarchy, then add top-level view controllers in order.
Most clients will not need to subclass UITabBarController.
If more than five view controllers are added to a tab bar controller, only the first four will display.
The rest will be accessible under an automatically generated More item.
UITabBarController is rotatable if all of its view controllers are rotatable.
*/
NS_CLASS_AVAILABLE_IOS(2_0) #interface UITabBarController : UIViewController <UITabBarDelegate, NSCoding>
As well as under the Help Menu there's "Developer Documentation" (CMD + SHIFT + 0) which has a multitude of useful information.

awakeFromNib gets called twice and outlets inside viewcontroller are not set

I am trying to build a menubar application for OSX.
I have a AppDelegate, a Storyboard and a ViewController.
The storyboard contains a application scene with the AppDelegate. The AppDelegate sets up the whole view hierarchy. Inside the AppDelegate I define a NSPopover with a ContentViewController(My ViewController). In order to set the ContentViewController I load the ViewController from the storyboard with the following code:
NSStoryboard*board=[NSStoryboard storyboardWithName:#"Main" bundle:nil];
_ruleView.contentViewController=[board instantiateControllerWithIdentifier:#"egm"];
When I use the debugger to examine the awakeFromNib method the outlets inside the ViewController are not set why?
The next problem is when the NSStatusItem is clicked it shows the NSPopover and the NSPopover calls the awakeFromNib inside my ViewController again and then my application crashes.
Here is the code of the AppDelegate:
#import "AppDelegate.h"
#import "ViewController.h"
#interface AppDelegate()
{
NSStatusItem*_statusItem;
NSPopover*_ruleView;
}
-(void)statusItemButtonPressed:(id)sender;
-(void)openPopupWindow;
-(void)closePopupWindow;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
}
-(id)init
{
self=[super init];
if(self)
{
_ruleView=[[NSPopover alloc] init];
NSStoryboard*board=[NSStoryboard storyboardWithName:#"Main" bundle:nil];
_ruleView.contentViewController=[board instantiateControllerWithIdentifier:#"egm"];
_statusItem=[[NSStatusBar systemStatusBar] statusItemWithLength:24];
_statusItem.button.title=#"EG";
_statusItem.button.action=#selector(statusItemButtonPressed:);
}
return self;
}
-(void)statusItemButtonPressed:(id)sender
{
if(!_ruleView.shown)
{
[self openPopupWindow];
}
else
{
[self closePopupWindow];
}
}
-(void)openPopupWindow{
[_ruleView showRelativeToRect:NSZeroRect ofView:_statusItem.button preferredEdge:NSMinYEdge];
}
-(void)closePopupWindow{
[_ruleView close];
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
I figured out that awakeFromNib can be called several times while unarchiving the view hierarchy from a nib. A better alternative is viewDidLoad which ensures that the whole view hierarchy is loaded and ready.

How do I present UIViewController from SKscene with social framework?

I am making a game like Flappy Bird. How do I present a UIViewController from SKScene?
First of all, I tell my environments
Mac OS X 10.9
Xcode 5.0.2
Sprite Kit(framework), social.framework(framework) are added in my project
My goal is to display a "Share" button upon Game Over. Tapping the share button image should present a SLComposeViewController (Twitter Share). The contents of the scene should not change.
I'd like to solve bellow issue and change display from GameOverScene to tweetSheet(display)
composed with social.framework.
The issue
[self presentViewController:tweetSheet animated:YES completion:nil];
//Error:No visible #interface for 'GameOverScene' declares the selector "presentViewController":animated:completion:
My coding files are below(I extracted parts of important codes).
ViewController.h
import <UIKit/UIKit.h>
import <SpriteKit/SpriteKit.h>
import <iAd/iAd.h>
#interface ViewController : UIViewController<ADBannerViewDelegate><br>
#end
GameOverScene.h
#import <SpriteKit/SpriteKit.h>
#class SpriteViewController;
#interface GameOverScene : SKScene {
}
#end
GameOverScene.m
#import "GameOverScene.h"
#import "NewGameScene.h"
#import "MainScene.h"
#import <Social/Social.h>
#implementation GameOverScene {
//The twitter button
SKSpriteNode *_twitterbutton;
}
- (id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size]) {
//Creating the twitterbutton with the twitterbutton image from Images.xcassets
_twitterbutton = [SKSpriteNode spriteNodeWithImageNamed:#"twitterbutton"];
[_twitterbutton setSize:CGSizeMake(50, 50)];
[_twitterbutton setPosition:CGPointMake(self.size.width/2, self.size.height/5 + 50)];
//Adding the twitter button
[self addChild:_twitterbutton];
//Again, this is important, otherwise we can't identify what button is pressed
_twitterbutton.name = #"twitterbutton";
[_twitterbutton setPosition:CGPointMake(self.size.width/2, self.size.height/5 + 50)]
}
return self;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//Same as in NewGameScene menu
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
//Is the twitter button touched?
if([node.name isEqualToString:#"twitterbutton"]){
if ([SLComposeViewController isAvailableForServiceType:SLServiceTypeTwitter]){
SLComposeViewController *tweetSheet = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeTwitter];
[tweetSheet setInitialText:#"TestTweet from the Game !!"];
[self presentViewController:tweetSheet animated:YES completion:nil];
**//Error:No visible #interface for 'GameOverScene' declares the selector "presentViewController":animated:completion:**
}
}
ViewControlloer.m
#import "ViewController.h"
#import "NewGameScene.h"
#implementation ViewController
//Loads the view onto our main class
- (void)loadView
{
self.view = [[SKView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
}
//Executes when view finishes loading
- (void)viewWillLayoutSubviews
{
[super viewDidLoad];
//Set the resize mode to flexible width and height
[self.view setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
//Create our view from our original view
//Make sure to leave originalContentView in, otherwise the app will crash
SKView *skView = (SKView *)self.originalContentView;
//We create a new NewGameScene according to the current dimensions
SKScene *scene = [NewGameScene sceneWithSize:skView.bounds.size];
//Create a transition class with animation type fade and a duration of .4 seconds
SKTransition *transition = [SKTransition fadeWithDuration:.4];
//Present the menu view (NewGameScene) with our fade in transition
[skView presentScene:scene transition:transition];
}
#end
You cannot present a viewController from within a SKScene as it is actually only being rendered on a SKView. You need a way to send a message to the viewController, which in turn will present the viewController. For this, you can use delegation.
Add the following protocol definition to your SKScene's .h file:
#protocol sceneDelegate <NSObject>
-(void)showShareScreen;
#end
And declare a delegate property in the interface:
#property (weak, nonatomic) id <sceneDelegate> delegate;
Then, at the point where you want to present the share screen, instead of the line:
[self presentViewController:tweetSheet animated:YES completion:nil];
Use this line:
[self.delegate showShareScreen];
Now, in your viewController's .h file, implement the protocol:
#interface ViewController : UIViewController <sceneDelegate>
And, in your .m file, add the following line before you present the scene:
scene.delegate = self;
Then add the following method there:
-(void)presentShareScreen
{
if ([SLComposeViewController isAvailableForServiceType:SLServiceTypeTwitter])
{
SLComposeViewController *tweetSheet = [SLComposeViewController
composeViewControllerForServiceType:SLServiceTypeTwitter];
[tweetSheet setInitialText:#"TestTweet from the Game !!"];
[self presentViewController:tweetSheet animated:YES completion:nil];
}
}
An alternate method would be to use NSNotificationCenter
Keep the -presentShareScreen method as described in the previous alternative.
Add the viewController as a listener to the notification in it's -viewDidLoad method:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(presentShareScreen) name:#"showShareScreen" object:nil];
Then, in the scene at the point where you want to show this viewController, use this line:
[[NSNotificationCenter defaultCenter] postNotificationName:#"showShareScreen" object:nil];

How can I redraw an image in an custom NSView for an NSStatusItem?

I am having some troubles understanding how to wire a custom NSView for an NSMenuItem to support both animation and dragging and dropping. I have the following subclass of NSView handling the bulk of the job. It draws my icon when the application launches correctly, but I have been unable to correctly setup the subview to change when I invoke the setIcon function from another caller. Is there some element of the design that I am missing?
TrayIconView.m
#import "TrayIconView.h"
#implementation TrayIconView
#synthesize statusItem;
static NSImageView *_imageView;
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
statusItem = nil;
isMenuVisible = NO;
_imageView = [[NSImageView alloc] initWithFrame:[self bounds]];
[self addSubview:_imageView];
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect
{
// Draw status bar background, highlighted if menu is showing
[statusItem drawStatusBarBackgroundInRect:[self bounds]
withHighlight:isMenuVisible];
}
- (void)mouseDown:(NSEvent *)event {
[[self menu] setDelegate:self];
[statusItem popUpStatusItemMenu:[self menu]];
[self setNeedsDisplay:YES];
}
- (void)rightMouseDown:(NSEvent *)event {
// Treat right-click just like left-click
[self mouseDown:event];
}
- (void)menuWillOpen:(NSMenu *)menu {
isMenuVisible = YES;
[self setNeedsDisplay:YES];
}
- (void)menuDidClose:(NSMenu *)menu {
isMenuVisible = NO;
[menu setDelegate:nil];
[self setNeedsDisplay:YES];
}
- (void)setIcon:(NSImage *)icon {
[_imageView setImage:icon];
}
TrayIconView.h
#import <Cocoa/Cocoa.h>
#interface TrayIconView : NSView
{
BOOL isMenuVisible;
}
#property (retain, nonatomic) NSStatusItem *statusItem;
- (void)setIcon:(NSImage *)icon;
#end
The solution to this problem was actually outside of the view detailed here. The caller of the interface was being double instantiated on accident, thus nulling out the reference to the previously created NSView. After correcting that concern the app draws and works just fine.
With regard to dragging, I just implemented a subclass of NSView that implemented the Cocoa draggable protocol and added it as a subview to this parent class. That allows dragging onto the currently established NSRect that contains the menubar icon.

How to replace current viewController with a new viewController

I'm trying to replace my current viewController with a new one. I've been able to do this before but I'm having some issues with BAD_ACCESS.
This is the code that will run when I want to replace the current view with a new one.
(The function will be called using a local property "self.some_data" (nonatomic, retain))
-(void) labelSelected:(SomeDataObject*) some_data{
SomeViewController *viewController = (SomeViewController*)[[ClassManager sharedInstance] viewControllerForClassIdentifier:#"com.somename" fromPlistFileName:#"iPhoneScreenList"];
viewController.data = (NSObject*)some_data;
[some_data retain];
//[self.navigationController pushViewController:viewController animated:YES];
UINavigationController *tempNavigationController = self.navigationController;
[[self retain] autorelease];
[tempNavigationController popViewControllerAnimated:FALSE];
[tempNavigationController pushViewController:viewController animated:TRUE];
}
Here everything works fine. The issue is that if I release the new "viewController" it crashes. And if I choose:
[tempNavigationController popViewControllerAnimated:TRUE];
I get some really wierd behaviour where the controller never gets replace and I return to the rootController and the navigation bar has two layers of text on it.
And if I do this:
[tempNavigationController pushViewController:viewController animated:FALSE];
I get BAD_ACCESS and the application chrashes. It worked before but not anymore.
What am I doing wrong?
Thanks!
Use category for controller replace:
// UINavigationController+ReplaceStack.h
#interface UINavigationController (ReplaceStack)
- (void) replaceLastWith:(UIViewController *) controller;
#end
// UINavigationController+ReplaceStack.m
#import "UINavigationController+ReplaceStack.h"
#implementation UINavigationController (ReplaceStack)
- (void) replaceLastWith:(UIViewController *) controller {
NSMutableArray *stackViewControllers = [NSMutableArray arrayWithArray:self.viewControllers];
[stackViewControllers removeLastObject];
[stackViewControllers addObject:controller];
[self setViewControllers:stackViewControllers animated:YES];
}
#end