I am using Xcode 4.5 and targeting iOS 5 and above.
I have a popover that allows a user to change the background of the underlying view.
When tapping on the "button", it requires that I tap twice to see the change.
I am using NSNotification. I have tried Delegation, yet it does nothing.
Any help or tips would be greatly appreciated.
From viewWillAppear:
// nav button is for background image selection
navigationBtn.tag = buttonTag;
[navigationBtn setBackgroundImage:[UIImage imageNamed:tempImage] forState:UIControlStateNormal];
if (selectFlag) {
[navigationBtn addTarget:self action:#selector(BGSelected:) forControlEvents:UIControlEventTouchUpInside];
} else {
navigationBtn.adjustsImageWhenHighlighted = NO;
}
And the method for selection:
- (void)BGSelected:(id)sender {
self.bgtag = [sender tag];
SettingsDAO *settingsDao = [[SettingsDAO alloc] init];
if ([self.currentView isEqualToString:#"New_Journal"])
{
NSLog(#"Update Journals Background");
[settingsDao updateJournalBackgroundId:self.journalId withBackgroundId:self.bgtag];
}
// notification to let EntryView know the background has changed
[[NSNotificationCenter defaultCenter] postNotificationName:#"aBackgroundChanged"
object:self];
[settingsDao release];
}
the NSNotification Method:
- (void)aBackgroundChanged:(NSNotification *)notification {
[self invalidate];
[self reloadSettings];
}
If you are using storyboard, and your popover is a segue you can do like that:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// perform all the operations...ecc
[[(UIStoryboardPopoverSegue*)segue popoverController] setDelegate:self];
}
Then you have to make sure your caller controller has all the delegate methods.
Or, with NSNotification:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// perform all the operations...ecc
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(changeBackground:)
name:BackgroundHasChangedNotification
object:[segue destinationViewController]];
}
Then just remember to remove observer in dealloc, and in change background method:
-(void)changeBackground:(NSNotification*)notification {
NSDictionary *userInfo = notification.userInfo;
// I assume you are using background name
NSString *backgroundName = [userInfo objectForKey:BackgroundOneConstant];
// do the rest....
}
Related
The scene auto pauses on applicationWillResignActive and auto unpauses when applicationDidBecomeActive is run. I am looking to either have the scene pause on applicationWillResignActive via the nsnotification and not auto resume when applicationDidBecomeActive is run. Any ideas? Thanks in advance.
AppDelegate
- (void)applicationDidBecomeActive:(UIApplication *)application {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
[[NSNotificationCenter defaultCenter] postNotificationName:#"backgroundPause" object:nil];
}
GameViewController
- (void)handleNotification:(NSNotification *)notification {
if ([notification.name isEqualToString:#"backgroundPause"]) {
SKView *skView = (SKView *)self.view;
skView.scene.paused = YES; //pauses scene
[self.lblPaused removeFromSuperview];//removes any lingering pause menu items
[self.lblPausedHelp removeFromSuperview];
self.lblPaused = [[UILabel alloc] init];
self.lblPaused.center = CGPointMake(self.view.frame.size.width/2 - 125, self.view.frame.size.height/2 - 40);
self.lblPaused.text = #"PAUSED";
[self.lblPaused setFont:[UIFont boldSystemFontOfSize:66]];
[self.lblPaused sizeToFit];
self.lblPaused.textColor = [UIColor blackColor];
[self.view addSubview:self.lblPaused];//adds pause label
self.lblPausedHelp = [[UILabel alloc] init];
self.lblPausedHelp.center = CGPointMake(self.view.frame.size.width/2 - 145, self.view.frame.size.height/2 + 40);
self.lblPausedHelp.text = #"tap anywhere to resume";
[self.lblPausedHelp setFont:[UIFont boldSystemFontOfSize:26]];
[self.lblPausedHelp sizeToFit];
self.lblPausedHelp.textColor = [UIColor blackColor];
[self.view addSubview:self.lblPausedHelp];//adds pause label
}
}
I'm pretty sure it's a bug in spritekit. No matter what you do, the game will unpause itself in applicationDidBecomeActive
I asked the same question here. pausing spritekit game on app launch / exit .. iOS8 You have to subclass SKScene and override the paused property to get it to work. It's weird you have to do that. It really shouldnt have so many problems, but thats the only way I could get my game to stay paused
EDIT: okay, I translated the code to objective-c. I hope this is useful to you because my objective-c was rustier than i expected.
AppDelegate.m
- (void)applicationWillResignActive:(UIApplication *)application {
[[NSNotificationCenter defaultCenter]postNotificationName:#"pauseGameScene" object:nil];
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
[[NSNotificationCenter defaultCenter]postNotificationName:#"stayPausedNotification" object:nil];
}
SKView Subclass
#interface MySKView : SKView
- (void) setStayPaused;
#end
#implementation MySKView
bool _stayPaused = false;
- (void) setPaused:(BOOL)paused{
if (!_stayPaused) {
super.paused = paused;
}
_stayPaused = NO;
}
- (void) setStayPaused{
_stayPaused = YES;
}
#end
GameViewController
#interface GameViewController : UIViewController
-(void)pauseGame;
#end
#implementation GameViewController
SKScene *_scene;
MySKView *_skView;
-(void)pauseGame{
_skView.paused = YES;
_skView.scene.view.paused = YES;
}
- (void)viewDidLoad
{
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(pauseGame) name:#"pauseGameScene" object:nil];
// Configure the view.
_skView = [[MySKView alloc]initWithFrame:self.view.frame];
_skView.showsFPS = YES;
_skView.showsNodeCount = YES;
/* Sprite Kit applies additional optimizations to improve rendering performance */
_skView.ignoresSiblingOrder = YES;
// Create and configure the scene.
_scene = [[GameScene alloc]initWithSize:_skView.frame.size];
_scene.scaleMode = SKSceneScaleModeAspectFill;
// Present the scene.
[self.view addSubview:_skView];
[_skView presentScene:_scene];
}
- (void)viewDidAppear:(BOOL)animated{
[[NSNotificationCenter defaultCenter]addObserver:_skView selector:#selector(setStayPaused) name:#"stayPausedNotification" object:nil];
}
#end
I have a video playing app where an NSView is shown, tracked to the mouse coordinates, to show the time position when the user hovers over a certain area.
This works perfectly 70% of the time, however it frequently doesn't fire at all. The times when this is most likely to occur seem to be when bringing the mouse inside the view for the first time and after hovering the mouse outside of the area and then back inside again.
The code inside the NSView subclass is as follows:
- (void)viewDidMoveToWindow
{
if ([self window]) {
[self resetTrackingRect];
}
}
- (void)clearTrackingRect
{
if (rolloverTrackingRectTag > 0)
{
[self removeTrackingRect:rolloverTrackingRectTag];
rolloverTrackingRectTag = 0;
}
}
- (void)resetTrackingRect
{
[self clearTrackingRect];
rolloverTrackingRectTag = [self addTrackingRect:[self visibleRect]
owner:self userData:NULL assumeInside:NO];
}
- (void)resetCursorRects
{
[super resetCursorRects];
[self resetTrackingRect];
}
- (void)mouseEntered:(NSEvent *)theEvent
{
// Only ask for mouse move events when inside rect because they are expensive
[[self window] setAcceptsMouseMovedEvents:YES];
[[self window] makeFirstResponder:self];
// Tells the observer to show the time view
[[NSNotificationCenter defaultCenter] postNotificationName:#"MWTimelineHover" object:self userInfo:[NSDictionary dictionaryWithObjectsAndKeys:theEvent,#"event",nil]];
}
- (void)mouseExited:(NSEvent *)theEvent
{
[[self window] setAcceptsMouseMovedEvents:NO];
[[self window] resignFirstResponder];
// Tells the observer to hide the time view
[[NSNotificationCenter defaultCenter] postNotificationName:#"MWTimelineHoverLeave" object:self];
}
- (void)mouseMoved:(NSEvent *)theEvent
{
[super mouseMoved:theEvent];
// Tells the observer to show the time view
[[NSNotificationCenter defaultCenter] postNotificationName:#"MWTimelineHover" object:self userInfo:[NSDictionary dictionaryWithObjectsAndKeys:theEvent,#"event",nil]];
}
Note: on the occasions when it stalls, mouseExited is not called and the view has not lost firstResponder status. I am also not dragging the mouse, simply moving it normally.
You need to use NSTrackingArea. Here is reference NSTrackingArea Class Reference.
- (void)commonInit {
CGRect rect = CGRectMake(0.0f, 0.0f, self.frame.size.width, self.frame.size.height);
NSTrackingAreaOptions options = NSTrackingActiveInKeyWindow | NSTrackingMouseMoved | NSTrackingInVisibleRect;
_trackingArea = [[NSTrackingArea alloc] initWithRect:rect options:options owner:self userInfo:nil];
[self addTrackingArea:_trackingArea];
}
Hope it helps you.
I am experimenting with this code.
In one of viewController's I am using next snippet:
- (BOOL)textView:(UITextView *)textView1 shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
if([text isEqualToString:#"\n"])
{
[textView resignFirstResponder];
return NO;
}
return YES;
}
I found in web to dismissing the keyboard by pressing returnButton. It works well when I am calling it from this viewController. In root KLNoteViewController I am adding notification in handle state changes-method:
- (void) setState:(KLControllerCardState)state animated:(BOOL) animated
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"refresh" object:self];
if (animated)
{
[UIView animateWithDuration:self.noteViewController.cardAnimationDuration animations:^{
[self setState:state animated:NO];
}];
return;
}
//Full Screen State
if (state == KLControllerCardStateFullScreen)
{
[self expandCardToFullSize: animated];
[self setYCoordinate: 0];
}
//Default State
else if (state == KLControllerCardStateDefault)
{
[self shrinkCardToScaledSize: animated];
[self setYCoordinate: originY];
}
//Hidden State - Bottom
else if (state == KLControllerCardStateHiddenBottom)
{
//Move it off screen and far enough down that the shadow does not appear on screen
[self setYCoordinate: self.noteViewController.view.frame.size.height + abs(self.noteViewController.cardShadowOffset.height)*3];
}
//Hidden State - Top
else if (state == KLControllerCardStateHiddenTop)
{
[self setYCoordinate: 0];
}
//Notify the delegate of the state change (even if state changed to self)
KLControllerCardState lastState = self.state;
//Update to the new state
[self setState:state];
//Notify the delegate
if ([self.delegate respondsToSelector:#selector(controllerCard:didChangeToDisplayState:fromDisplayState:)]) {
[self.delegate controllerCard:self
didChangeToDisplayState:state fromDisplayState: lastState];
}
}
and add adding observer:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(dismissKeyboard) name:#"refresh" object:nil];
in every viewControllers -viewDidLoad. So when this observer is used with UITextField everything works fine, in viewController when I am using UIText View I have crash with log:
[NSStackBlock isEqualToString:]: unrecognized selector sent to
instance 0xbfffd228
I searched in web but found only two links with explanation of what is the NSStackBlock is and they are not informative well for me to solve the problem. Can somebody explain what it can be?
I'm working on application for video streaming. It is built with ARC for iOS5.
To display view I use MPMoviePlayerViewController this way:
.h
#interface EpisodesTableViewController : UITableViewController<EpisodeUrlResolverDelegate> {
NSTimeInterval playbackTime;
EpisodeUrlResolver *episodeUrlResolver;
}
#property (strong, nonatomic) MPMoviePlayerViewController *player;
#end
.m
#implementation EpisodesTableViewController
#synthesize episodes, player;
- (void)viewDidLoad
{
[super viewDidLoad];
episodeUrlResolver = [[Soap4MeEpisodeUrlResolver alloc] init];
[episodeUrlResolver setDelegate:self];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(willEnterForeground) name:#"WillEnterForeground" object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(willEnterBackground) name:#"WillEnterBackground" object:nil];
}
- (void)viewDidUnload
{
episodeUrlResolver = nil;
player = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"WillEnterForeground" object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"WillEnterBackground" object:nil];
[super viewDidUnload];
}
- (void)url:(NSURL *)url WasResolvedForEpisode:(Episode *)episode {
player = [[MPMoviePlayerViewController alloc] initWithContentURL:url];
player.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
player.moviePlayer.allowsAirPlay = YES;
[self presentModalViewController:player animated:YES];
}
- (void)willEnterBackground {
if (player) {
playbackTime = player.moviePlayer.currentPlaybackTime;
}
}
- (void)willEnterForeground {
if (!player)
return;
if(player.moviePlayer.playbackState == MPMoviePlaybackStateInterrupted || player.moviePlayer.playbackState == MPMoviePlaybackStateStopped || player.moviePlayer.playbackState == MPMoviePlaybackStatePaused)
{
[self continuePlayback];
}
}
- (void)continuePlayback {
[self presentModalViewController:player animated:YES];
[player.moviePlayer setInitialPlaybackTime:playbackTime];
NSLog(#"%f", player.moviePlayer.initialPlaybackTime);
[player.moviePlayer play];
}
Few words about presented code:
when url for video is resolved I create the MPMoviePlayerViewController object for video playback. Playback is started. Then when app is going background with home button MPMoviePlayerViewController automatically pauses playback and dissapears from screen (weird behavior, as for me). Thats why I have to save playback time when app is going background and show existing MPMoviePlayerViewController again with restored playback time when app is back to foreground.
It works fine when user presses home button and then is going back to application.
But it doesn't work when user locks the device. WillEnterBackground notification is fired when
applicationWillEnterForeground is called, so for locking event it is called too.
But in case when user locks the device MPMoviePlayerViewController wasn't being dismissed properly as in situation when user presses home button. It is hidden from the screen, but calling the code
[self presentModalViewController:player animated:YES];
throws the exception
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Application tried to present modally an active controller <EpisodesTableViewController: 0x91b3950>.'
I tried to change the code to dismiss controller:
- (void)willEnterForeground {
if (!player)
return;
if(player.moviePlayer.playbackState == MPMoviePlaybackStateInterrupted || player.moviePlayer.playbackState == MPMoviePlaybackStateStopped || player.moviePlayer.playbackState == MPMoviePlaybackStatePaused)
{
if (self.presentedViewController) {
[self dismissViewControllerAnimated:YES completion:^{
[self continuePlayback];
}];
}
else {
[self continuePlayback];
}
}
}
Now it doesn't throw the exception, but displays warning
wait_fences: failed to receive reply: 10004003
Also completion block is never called.
How to properly to solve the background/foreground continue playing with modally displayed MPMoviePlayerViewController?
Thanks for replies.
I solved the problem.
Method -willEnterBackground should look like:
- (void)willEnterBackground {
if (player) {
playbackTime = player.moviePlayer.currentPlaybackTime;
[self dismissModalViewControllerAnimated:NO];
}
}
We have to dismiss MPMoviePlayerViewController manually when app is entering background mode.
Pay attention to use only
[self dismissModalViewControllerAnimated:NO];
instead of
[self dismissMoviePlayerViewControllerAnimated];
Thats because MPMoviePlayerViewController was shown with
[self presentModalViewController:player animated:YES];
I have a MPMoviePlayerController subclass that should show the controls when playback is finished. I have attached a responder to the MPMoviePlayerPlaybackDidFinishNotification notification and tried setting the control style as follows:
[self setControlStyle:MPMovieControlStyleEmbedded];
This is not working. In essence, at the end of the video I want to controls to show.
How can I show controls programatically?
NOTE: The controller is NOT in fullscreen mode.
Kindly find my full Code about this , it's working with me
add .h class add this
#property(strong,nonatomic) MPMoviePlayerViewController * moviePlayer;
at .m class add this code "pass the movie URl"
-(void) playMovie:(NSString *)filePath
{
NSURL *theOutputURL = [NSURL fileURLWithPath:filePath];
if(_moviePlayer)
[_moviePlayer.moviePlayer setContentURL:theOutputURL];
else
_moviePlayer = [[MPMoviePlayerViewController alloc] initWithContentURL:theOutputURL];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(myMovieFinishedCallback:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:_moviePlayer.moviePlayer];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(moviePlayerPlaybackStateDidChange:) name:MPMoviePlayerPlaybackStateDidChangeNotification object:_moviePlayer.moviePlayer];
if (![_moviePlayer.moviePlayer isPreparedToPlay])
[_moviePlayer.moviePlayer prepareToPlay];
_moviePlayer.moviePlayer.movieSourceType = MPMovieSourceTypeFile;
[_moviePlayer.moviePlayer setFullscreen:YES];
_moviePlayer.moviePlayer.controlStyle=MPMovieControlStyleEmbedded;
[_moviePlayer.moviePlayer setContentURL:theOutputURL];
_moviePlayer.view.frame = CGRectMake(0, 0, [[UIScreen mainScreen]bounds].size.width, [[UIScreen mainScreen]bounds].size.height);
[_moviePlayer shouldAutorotateToInterfaceOrientation: AVCaptureVideoOrientationLandscapeRight];
[self.view addSubview:_moviePlayer.view];
}
- (void) moviePlayerPlaybackStateDidChange: (NSNotification *) notification {
if (_moviePlayer.moviePlayer.playbackState == MPMoviePlaybackStateStopped) {
[_moviePlayer.moviePlayer setContentURL:[_moviePlayer.moviePlayer contentURL]];
[_moviePlayer.moviePlayer play];
}
}
-(void)myMovieFinishedCallback:(NSNotification*)aNotification
{
// to add your code after playback is finished
}