Resetting a view - iphone

When the user navigates to a different view and returns back to the original view I would like everything to be reset as though they were coming to the view for the first time.
I was able to stop the audio from playing when they leave, but not an animation method.
how would I be able to do this?
-(void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[audioIntro stop];
[mainAudio stop];
}
here's the animation code:
- (void) firstAnimation {
if (playSoundButton.enabled = NO || audioIntro.playing) {
return;
}
UIViewAnimationOptions options = UIViewAnimationOptionCurveLinear;
myImage.hidden = NO;
myImage.alpha = 1.0;
[UIView animateWithDuration:1.0 delay:12.0 options:options animations:^
{
setAnimationBeginsFromCurrentState:YES;
tapImage.alpha = 0.0;
}
completion:^(BOOL finished){
[self performSelector:#selector(secondAnimation) withObject:nil afterDelay:0.2];
}
];
}
thanks for any help.

It seems a bit hacky - but I would try this:
Use the "old style" animation - i.e. not with blocks - use "beginAnimation", setting an animationID for your animation.
In you viewWillDisappear method, call "beginAnimation" again, with the same animationID, but this time:
a. Set the animation duration to ZERO
b. Animate "to" the location where you want the stuff to reset to.
Set some sort of kill-flag in the viewWillDisappear method
Make the completion function look for the "kill flag" - and do not evoke the second animation inside your "secondAnimation" flag if the selector if the "kill flag" is set.
(I am a little unclear - if the "finished" flag would suffice for the "kill-flag" - not having seen your entire code.

Related

In ECSlidingViewController, how to disable the top view from sliding off screen

So I'm trying to implement the ECSlidingViewController sample into my App.
github source for this
The only thing I'd like to do to modify it, is prevent the TopView from completely sliding off-screen before it changes the contained view, and instead just keep the TopView in place but update it's contained view with it's new view controller that was selected from the menu. The Facebook app's take on this is exactly what I want, in case that sounded confusing.
After looking around for a while within the project, I've determined that it definitely (and obviously) has something to do with this line of code in the ECSlidingViewController.h:
- (void)anchorTopViewOffScreenTo:(ECSide)side animations:(void(^)())animations onComplete:(void(^)())complete;
I looked at where this gets called in the .m:
- (void)anchorTopViewOffScreenTo:(ECSide)side{
[self anchorTopViewOffScreenTo:side animations:nil onComplete:nil];
}
- (void)anchorTopViewOffScreenTo:(ECSide)side animations:(void(^)())animations onComplete:(void(^)())complete
{
CGFloat newCenter = self.topView.center.x;
if (side == ECLeft) {
newCenter = -self.resettedCenter;
} else if (side == ECRight) {
newCenter = self.screenWidth + self.resettedCenter;
}
[self topViewHorizontalCenterWillChange:newCenter];
[UIView animateWithDuration:0.25f animations:^{
if (animations) {
animations();
}
[self updateTopViewHorizontalCenter:newCenter];
} completion:^(BOOL finished){
if (complete) {
complete();
}
_topViewIsOffScreen = YES;
[self addTopViewSnapshot];
dispatch_async(dispatch_get_main_queue(), ^{
NSString *key = (side == ECLeft) ? ECSlidingViewTopDidAnchorLeft : ECSlidingViewTopDidAnchorRight;
[[NSNotificationCenter defaultCenter] postNotificationName:key object:self userInfo:nil];
});
}];
}
This is where I'm guessing the animation is being told how to animate, but I don't understand how any of this could be telling it to move off-screen. Am I overlooking something as simple as replacing something to nil? Perhaps there's another value somewhere that I haven't found? This is my first question on StackOverflow, and though I'm new to Obj-C in general, I have a pretty decent grasp on how it works. So I'm hoping to receive at least a tip in the right direction. Thanks!
Just use the following method
- (void)anchorTopViewTo:(ECSide)side animations:(void (^)())animations onComplete:(void(^)())complete
instead of
- (void)anchorTopViewOffScreenTo:(ECSide)side animations:(void(^)())animations onComplete:(void(^)())complete
If it's just the bouncing that you want to remove, then instead of calling:
[self anchorTopViewOffScreenTo:side animations:nil onComplete:nil];
call directly:
[self.slidingViewController resetTopView];

Strange memory leak in Window:addSubView

first of all sorry for my English :-) not so good.
I have a strange memory leak with the following code (code after the explanation).
I have a class, FLWaitingView. It is a simple view with a waiting indicator (plus a view with background), used to say to the user "wait for the data to be loaded".
It has two simple methods: show and dismiss.
In the show method, I find the main Application Window and add the subviews (the waiting view and a background view, with different animations). In the dismiss method, I remove it from superview.
In every show, I verify that the view isn't already visible using a static bool var (is_visible).
The strange thing is this: In the dismiss method, I use:
[self.view removeFromSuperview];
[self.waitingView removeFromSuperview];
to remove the two views from the Window, to avoid them to be retained. They are correctly removed, I can verify this with NSLog (for cicle on each window subview). But, in INSTRUMENTS, using the "mark heap" function, I see that in every single reload (new instance of FLWaitingView, then show, then dismiss) the old instance remains in memory and continues to increase memory usage. Obviously is not a problem of the calling code, because I correctly release the object:
//CALLING CODE
//customWaitingView is a property retained
self.customWaitingView = [[[FLWaitingView alloc]init]autorelease];
[self.customWaitingView show];
Moreover, and I think that this is the most important information, if I move the view dismission in another method, called by a selector, the leak disappear!!!
Now I show the "wrong" code and, after, the "correction". I would like to understand why it happens.
- (void)show
{
if (!is_visible){
id appDelegate = [[UIApplication sharedApplication] delegate];
UIWindow *window = [appDelegate window];
self.waitingLabel.text = #"Attendere";
self.view.alpha = 1.0;
self.waitingView.alpha = 1.0;
[window addSubview:self.view];
[window addSubview:self.waitingView];
[self.waitingIndicator startAnimating];
self.view.frame = window.frame;
self.waitingView.center = window.center;
// "Pop in" animation for alert
[self doPopInAnimationWithDelegate:self];
// "Fade in" animation for background
[self doFadeInAnimation];
is_visible = YES;
} else {
NSLog(#"FLWaitingView %# already visible, do nothing", self);
}
}
- (void)dismiss
{
[UIView beginAnimations:nil context:nil];
self.view.alpha = 0.0;
self.waitingView.alpha = 0.0;
[UIView commitAnimations];
[self.waitingIndicator stopAnimating];
//here is the problem
[self.view removeFromSuperview];
[self.waitingView removeFromSuperview];
is_visible = NO;
}
the code above is the "wrong" one, but if I add
[self performSelector:#selector(alertDidFadeOut) withObject:nil afterDelay:0.5];
in the dismiss method and a new method (obviously removing the redundant code from dismiss method):
- (void)alertDidFadeOut
{
//here the memory is correctly released
[self.view removeFromSuperview];
[self.waitingView removeFromSuperview];
is_visible = NO;
}
the memory is correctly released.
Why??????
Thank you in advance
Fabio
Your view isn't getting released as you would be expecting because at the moment you're releasing it there are still animations linked to it. You can only properly release it after the animations are finished.
Your second method works because the animation lasts less than 0.5 seconds - the releasing code is called after view is freed of all the animations.
Proper way to animate the view would be to either create an animation and assign its delegate or maybe a bit more elegant soulution is to use block-based animation like this:
- (void)dismiss
{
[[UIApplication sharedApplication] beginIgnoringInteractionEvents];
[UIView animateWithDuration: 0.15
animations: ^{
self.view.alpha = 0.0;
self.waitingView.alpha = 0.0;
}
completion: ^(BOOL finished){
[self.waitingIndicator stopAnimating];
    [self.view removeFromSuperview];
    [self.waitingView removeFromSuperview];
    is_visible = NO;
[[UIApplication sharedApplication] endIgnoringInteractionEvents];
}];
}

Triggering code when UIPickerView selectRow:inComponent:animated:YES is completed?

My action method:
- (IBAction)buttonPressed {
// animate picker to random row (picker is a UIPickerView)
int row = random() % [self.column1 count];
[picker selectRow:row inComponent:0 animated:YES];
[picker reloadComponent:0];
// display new selected row content
int selectedRow = [picker selectedRowInComponent:0];
NSString *selectedItem = [self.column1 objectAtIndex:selectedRow];
myLabel.text = selectedItem; // UILabel under the picker
}
However
myLabel.text = selectedItem;
gets called before the animation has completed and so it doesn't display the new value.
I found the thread How to get callback from UIPickerView when the selectRow animation is done?, but the answer uses a beginAnimations/commitAnimation block - about which apple says: "Use of this method is discouraged in iOS 4.0 and later. You should use the block-based animation methods to specify your animations instead."
How would I use block-based animation to accomplish this?
It looks like Apple needs to update their UIPickerView API to support a block based completion handler. Until then, just wait the estimated time of the animation.
[picker selectRow:0 inComponent:0 animated:YES];
[self performSelector:#selector(setMyLabel) // setMyLabel - my function
withObject:nil
afterDelay:0.4];
Have you tried something like this:
[UIView animateWithDuration:2.0
delay:0.0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
// the animation code
[myPickerView selectRow:0 inComponent:0 animated:YES];
}
completion:^(BOOL finished) {
[self performSelector:#selector(setMyLabel)];
}
];
Or you could just set the label directly in the completion block, if that's the only thing you want to do after the animation completes.
EDIT:
The code above is the same as the code in the other thread you've mentioned, but using animation blocks instead. I actually didn't know if the method selectRow:inComponent:animated: will run on the same animation block thread or it will have its own thread. Since it didn't work, that means it runs on its own thread so the code I wrote won't work.
There is one way around to solve this, which is by blocking the main thread for the time needed for the animation to complete. You can call [NSThread sleepForTimeInterval:1.0] after your call to selectRow:inComponent:animated:, and you can adjust the interval until you reach a suitable value. Note that blocking the main thread is highly discouraged by Apple. Personally I would block it only if I couldn't think of another way to achieve what I'm trying to do and if the blocking time is smaller than 1.

using completion with animateWithDuration causes exc_bad_access

I am trying to animate 2 UIButtons in a UITableViewCell called addToPlaylist and removeFromPlayList (they animate off to the right after being swiped on) and am using a block as follows
[UIView animateWithDuration:0.25 animations:^{
self.addToPlaylist.center = CGPointMake(contentsSize.width + (buttonSize.width / 2), (buttonSize.height / 2));
self.removeFromPlaylist.center = CGPointMake(contentsSize.width + (buttonSize.width / 2), (buttonSize.height / 2));
myImage.alpha = 1.0;
}
completion:^ (BOOL finished)
{
if (finished) {
// Revert image view to original.
NSLog(#"Is completed");
self.addToPlaylist.hidden = YES;
self.removeFromPlaylist.hidden = YES;
self.hasSwipeOpen = NO;
}
}];
on completion I want to hide the buttons to attempt to lessen redraw on scroll etc.
This code sits within '-(void) swipeOff' which is called in the UITableViewControllers method scrollViewWillBeginDragging like so:
- (void)scrollViewWillBeginDragging:(UIScrollView *) scrollView
{
for (MediaCellView* cell in [self.tableView visibleCells]) {
if (cell.hasSwipeOpen) {
[cell swipeOff];
}
}
}
The problem is the completion code, if I remove it or set it to nil all is good, if I include it I get an EXC_BAD_ACCESS. even if I include it with any or all of the lines within the if(finished) commented out
Am I using this in the wrong way, any help much appreciated.
Thanks
I had the same problem with animations. I've solved it by removing -weak_library /usr/lib/libSystem.B.dylib from Other Linker flags.
Also, according to this answer, if you need this flag, you may replace it with -weak-lSystem.
Check if you are not calling a UIView (collectionView, Mapview, etc) from inside the UIView block, meaning, it would be a call outside the main thread. If you are, try this:
DispatchQueue.main.async {
self.mapBoxView.setZoomLevel(self.FLYOVERZOOMLEVEL, animated: true
)}

Detecting when camera's iris is open on iPhone

For a cutom camera overlay I need to find out, when the iris is opened, because my overlay will allways shown while the iris is close (and then animating to open).
Any ideas ?
You can listen for the PLCameraViewIrisAnimationDidEndNotification notification. Since this is not officially documented, you might be in violation of the Apple TOS, but I think so long as you write your code so that it's defensive against the possibility that the name or contract of this notification might change (so in the future you might not get the event) you'll probably be ok. In other words, use a timer or other technique to ensure that the thing you want done when the iris is open will definitely happen eventually even if you never get the notification...
Trivial example without the defensive programming. (Of course, you can register an interest only for this specific notification as well, see the docs for the notification center.)
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(notificationCallback:)
name:nil
object:nil
];
- (void) notificationCallback:(NSNotification *) notification {
if ([[notification name] isEqualToString:#"PLCameraViewIrisAnimationDidEndNotification"]) {
NSLog(#"Iris open");
// we don't need to listen any more
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
}
It seems that PLCameraViewIrisAnimationDidEndNotification no longer gets notified in iOS5.
I can't figure out what is a suitable solution when the iris has finished opening, there must another option rather than using a 3 second timer.
Check here: https://devforums.apple.com/message/561008#561008
I have a ViewController (ALImagePickerController) which holds, initializes and presents the UIImagePickerController as a child view controller (I have another child view controller for presenting the taken image which is not shown here) and I present (as a modal) the ALImagePickerController when I want to use the camera. So during this the viewDidAppear of the ViewContoller I add an animation to bring in the camera overlay gracefully as the shutter animation disappears.
#interface ALImagePickerController ()
#property (nonatomic) UIImagePickerController *cameraController;
#property (nonatomic) CameraOverlayView *overlayView;
....
#end
#implementation ALImagePickerController
....
- (void)viewDidLoad {
[super viewDidLoad];
[UIApplication sharedApplication].statusBarHidden = YES;
self.cameraController = [UIImagePickerController new];
self.cameraController.sourceType = UIImagePickerControllerSourceTypeCamera;
self.cameraController.delegate = self;
self.cameraController.allowsEditing = NO;
self.cameraController.showsCameraControls = NO;
....
self.overlayView = [CameraOverlayView new];
....
self.overlayView.alpha = 0;
self.cameraController.cameraOverlayView = self.overlayView;
....
// add as child view controller
[self addChildViewController:self.cameraController];
[self.view addSubview:self.cameraController.view];
[self.cameraController didMoveToParentViewController:self];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[UIApplication sharedApplication].statusBarHidden = NO;
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// smoothly bring in the overlay as the native camera shutter animation opens.
[UIView animateWithDuration:0.2 delay:0.3 options:UIViewAnimationCurveEaseOut animations:^{
self.overlayView.alpha = 1.f;
} completion:nil];
}
....
#end
The way I solved this problem is I initialize all the elements with the hidden property set to YES, then call a 3-second delayed selector after I call the camera, where I set all the elements to hidden = NO. It's not an ideal solution but it seems to work, and any lag after the iris is opened is negligible.
You should already know when the camera is ready to take a picture. At least the way I use a custom camera overlay, I init the view with something like self.sourceType = UIImagePickerControllerSourceTypeCamera; and the other usual setup, and the camera is ready (or "iris is open") at that point.
In summary, if one is using a custom camera overlay the way I am used to using it, one will know when the iris is open because it is under your control.