Adding subview then releasing it: "modifying view that is being finalized" - iphone

I've got a UIButton called tagButton and a UIScrollView called tagsView.
Inside my -(void)renderTags method, I do
[[self.tagsView subviews] makeObjectsPerformSelector:#selector(removeFromSuperview)];
...to clear the decks, then I walk through a pair of arrays called allTags and activeTags, alternately creating "lit" and "unlit" buttons for each tag the user has ever used in the app. So far so good. But when I add the newly created UIButton to my view, thusly:
[self.tagsView addSubview:tagButton];
[tagButton release];
...the first time, nothing happens on the view, the tag button doesn't show up. The second time I put that same string in (which fires -toggleTag rather than -createTag, but ends up in the same rendering method), I get the console message modifying layer that is being finalized = 0x82b3ec0.
If I comment out [tagButton release], though, everything works. So, what gives? I thought adding a subview to a superview made the superview retain it. I'm not going to deal with this button again except to tell it to remove itself next time I render my tag list, so it seems like the view controller ought to release it.

Your problem is probably that you arent retaining tagButton to begin with, therefore there is no need to release it...hope that helps

Related

UIImage animations don't work in a view pushed without animation

I've got a view controller whose view contains a UIImageView that does animation:
//AnimationViewController::ViewDidLoad event:
var ctlAnimations = new UIImageView();
ctlAnimations.AnimationImages = list.ToArray(); //<--list contains the UIImages
ctlAnimations.AnimationDuration = 1.0 * list.Count;
ctlAnimations.StartAnimating();
this.Add(ctlAnimations);
This works perfectly: when I push AnimationViewController onto the navigation stack, it displays and animates the UIImage.
But now I need to show AnimationViewController with a custom animated transition:
var transition = CATransition.CreateAnimation ();
transition.Duration = 0.3f;
transition.TimingFunction = CAMediaTimingFunction.FromName(CAMediaTimingFunction.EaseInEaseOut);
transition.Type = CATransition.TransitionFade;
this.View.Layer.AddAnimation (transition, "fade");
//viewController is being pushed with animated=false, because we have a custom animation
base.PushViewController (viewController, false);
this.View.Layer.RemoveAnimation("fade");
This also works perfectly, in that the new View transitions into place using the specified custom animation.
But when I push AnimationViewController onto the stack using an animated transition, it displays but the animation doesn't run. Instead, it shows the first frame of the animation (the first image in the list), and doesn't run it.
So, something about the transition is breaking the ability to animate a UIImage in the new view controller, but I simply can't figure out what to do about it.
Update: I've noticed that if I tap the NavigationController's back button, but then move off of the back button and let go (so I don't actually go back), the animation starts playing!
PushViewController works like this: Over the current view controller the next view controller is placed you can say pushed onto the stack. From Apple docs its clear that either you need to push view controllers either with animation or without.
Work around:
Set the frame of the next view controller's view's x position beyond
the screen's right
Suppose width of the screen is 320, then set the x position of next
view as 320.
Add the next view as subview to the existing one.
Now do your custom animation.
Another work around:(a bit more overhead though)
Take a snapshot programmatically of current view.
Add the snapshot image as the initial view of next view controller.
Now push view controller without animation. (User will still see the old view)
In viewDidAppear of new view controller start your custom animation.
[I have to warn you that this method of taking snapshot might give you a small delay in older devices. Newer devices are pretty fast enough you wont see any lag]
Let me know if any issues in case you are implementing any of these solutions.
Try putting the animating bit in ViewDidAppear rather than ViewDidLoad. Also, try using breakpoints and NSLogs to follow what happens after the animation, starting with the ViewDidLoad and ViewDidAppear. Try having the animation repeat forever so you can see if it has ever been animating or not.
I'm very curious what the culprit is here as well. Why is the animation not displaying correctly in some cases?
My theory is that you have placed animation code in viewWillAppear rather than viewDidAppear. Animation code does not run properly when placed in WILL or SHOULD methods.
Can you please post back what caused the issue?
Suspicion #1
I am betting that your code is not being called because it is in ViewDidLoad. I believe you are creating a customized view stack, that means you need to be using the ChildViewController methods from Cocoa.
I am unfamiliar with MonoTouch (I only write pure CocoaTouch), so this might not be 100% correct
I would be consoling out your viewDidLoad and viewDidAppear methods and absolutely make sure they are being called. It is my suspicion that viewDidLoad IS NOT. And this is causing viewDidLoad to not be called on the UIImageView.
In your code you probably need the equivalent of (from objective-c):
[self addChildViewController:viewController];
// OR?
[base addChildViewController:viewController];
This tells the 'parent' viewController that the Child has been made visible, so call the viewDidLoad/Appear and Unload/Disappear methods when appropriate. This might not exist in MonoTouch, or the Push methods might not be fully implemented, so you might need to do some hacky (bad) stuff like manually calling the viewDidLoad method manually.
Suspicion #2
It could also be that your 'list' variable (the one holding the images) is nil. If that happened the animation would not run. OR maybe it has something to do with the duration of your animation, try to set it to whatever would make it repeat forever. Be sure it isn't running REAL FAST somehow and you are just missing it.
begin philosophical musing
Either that or start learning actual Cocoa development :) Not meant as a flame, but definitely meant seriously, you are going to run into problems trying to develop applications through translation layers (custom language-bridges meant to get around writing the base language of a framework/application/platform).
Titanium/MonoTouch/PhoneGap will never produce as robust or high-quality applications as real Objective-C. And besides that, once you learn Cocoa it will change how you write everything else, and I doubt you will want to go back. As the wonderful website of the same name says, 'Cocoa is my girlfriend'
Let me tell the something about UI in IOS. In IOS access to the UI Elements is limited to a single thread.
The single thread would always be the mainThread except in the case when you are running an animation.
Hence when u are performing number of animation at the same instance you have to use
beginAnimation.
setFrame (or) some methods that changes state of UI element.
Repeat step2 for all those objects u are scheduling to animate.
comitAnimations to perform all animations at once. (using comit animations ensure all the animations are performed on same thread)
So, I guess here is what happening in ur case.
Viewcontroller started an animation to push the view controller into stack.
Image view started another animation before finishing first animation.
Look at the links to get a clear idea link1 and link2.
Well Let's get into the solution
Add an ivar and retained property named ctlAnimations to your class
In ViewDidLoad (or) ViewDidAppear
self.ctlAnimations = new UIImageView();
ctlAnimations.image=(UIImage*)[list.toArray() objectAtIndex:0];
this.Add(ctlAnimations);
[self performSelector:#selector(startAnimatingImage) afterDelay:0.1];
Create a private method named startAnimatingImage with below code
self.ctlAnimations.AnimationImages = list.ToArray();
ctlAnimations.AnimationDuration = 1.0 * list.Count;
ctlAnimations.StartAnimating();
On a brief we just shown first image for a moment when the firstanimation is taken place and then we delayed animation start for 1 second so that it performs after first animation and then starts animating the image.
Go ahead and give it a try
There is the concept for the Main thread. So UIAnimation are actually works on Main Thread and at the same time may be this was happen that another task is performing on the same Main Thread.So that was a case like IOS will give preference according to processes id of each process at a time and for the solution you need to make background thread for the same operations.
Well, I never figured out what the problem was here, but it also turned out that there was some sort of memory leak in UIImage such that on some phones my code was crashing.
So I rewrote it to do the animation manually. Now I set a timer and keep a frame index variable, and every time the timer rings I change the image myself. This amounts to the same thing, and it turns out it fixes this problem.

UIKeyboardWillShowNotification issue

This is the flow of my app. 1st view -> 2nd View -> 3rd View
On 3rd view, when I click on any row of tableView one UIView gets displayed, which has one textField which accepts only numbers. For this I have implemented UIKeyboardWillShowNotification and displayed a UIButton for 'dot' button on the down-left corner of keyboard (For this I have created two images and sets that image to UIButton object).
My problem is, After using this custom keyboard(for 2-3 times), when I redirect form 3rd view to 1st View, This UIButton (with dot image) is appearing on 1st view. I have used default keyboard at there but this image not getting away.
While moving from 3rd view to 1st view, I m removing Observer for the keyboard notification which I have registered earlier & also I m checking wether,
if ([dotButton retainCount] > 0) {
[dotButton release];
dotButton = nil;
}
I have allocated dot button only once in viewDidLoad.
I m using popToRootViewController method to go back to 1st view from 3rd view.
I dont want to display this dot button on my 1st view. How can I do this.
Follow this steps
1) First make the doneButton an instance varible of your class, this will help u maintain the reference to the button
2) Add this code at the beginning of ur keyboardWillShow:(NSNotification *)note method
if(dotButton){
[dotButton removeFromSuperview];
dotButton = nil;
}
and one more thing implement UIKeyboardWillHideNotification method with NSNotificationCenter and perfrom step 2 over there.
I assume when you created the dotButton, you are calling addSubview: to put it on the screen.
When you want to remove it, you need to remove by calling [dotButton removeFromSuperview]. If you just release it, it will still be retained by the view that is containing it.
Finally, you should NEVER be calling retainCount unless you are debugging something. I've been writing Objective-C code for years and I have NEVER used retainCount, even when I was doing weird runtime stuff.
The rule is simple. If you need an object to stick around, you call retain. When you are done with it, you call release. If somebody else has retained it that's none of your business.

Is there any seriously good reason why a view can not completely manage itself?

Example: I have an warning triangle icon, which is a UIImageView subclass. That warning is blended in with an animation, pulses for 3 seconds and then fades out.
it always has a parent view!
it's always only used this way: alloc, init, add as subview, kick off animation, when done:remove from superview.
So I want this:
[WarningIcon warningAtPosition:CGPointMake(50.0f, 100.0f) parentView:self];
BANG!
That's it. Call and forget.
The view adds itself as subview to the parent, then does it's animations. And when done, it cuts itself off from the branch with [self removeFromSupeview];. And to make sure the procedure can finish really after this, make a nifty -retain and -autorelease couple so the whole thing can complete and return before it really gets trashed.
Now some nerd a year ago told me: "Never cut yourself off from your own branch". In other words: A view should never ever remove itself from superview if it's no more referenced anywhere.
I want to get it, really. WHY? Think about this: The hard way, I would do actually the exact same thing. Create an instance and hang me in as delegate, kick off the animation, and when the warning icon is done animating, it calls me back "hey man i'm done, get rid of me!" - so my delegate method is called with an pointer to that instance, and I do the exact same thing: [thatWarningIcon removeFromSuperview]; - BANG.
Now I'd really love to know why this sucks. Life would be so easy.
I have no objections to your solution—especially with the [[self retain] autorelease]. I'm using the same approach from time to time myself.
You should go and ask the no-branch-cutter-nerd about his reasoning (and post this information here).

IPHONE: Test to see if a view is on the superview

I am about to do a
[vistaX removeFromSuperview];
How do I test to see if vistaX is present on the super view before removing it? Removing a view that isn't there would lead to a crash on the application...
thanks for any help.
You can guard it with:
if(vistaX.superview)
[vistaX removeFromSuperview];
Although, I wasn't aware that removeFromSuperview would fail if there wasn't a superview. Are you sure that this is the issue and it isn't maybe related to the fact that removeFromSuperview releases the view?
EDIT:
Based on your comment below, it sounds like vistaX's retain count is going to 0 the first time around and it's being freed. If you don't want this to happen, add a property to your class that retains vistaX (i.e. "#property (retain)") so you can be sure that it'll always be around.
EDIT EDIT:
Do you have a handle on the superView or one of its ancestors? If so, I would recommend setting a unique tag on the vistaX view. This can be done programmatically or through IB. Then, use the viewWithTag selector on one of the ancestors to search for the vistaX view by its unique tag.
You Can Check it By applying one condition like
if(vistaX.superview!=nil)
[vistaX removeFromSuperview];
then if superview exists, it will remove from super view, otherwise not.

How do I create a reusable Loading Screen?

How do I create a loading screen that can be reused at any given time. I'm aware of the Default.png but I need the flexibility to plug in a loading screen at any point during the application life cycle.
This is what I have thus far.
//inside a method that gets called by a UIButton
LoadingViewController* loadController = [[LoadingViewController alloc] initWithNibName:#"Loading" bundle:nil vertical:NO];
[self.view addSubview: loadController.view];
//some method call that takes a few seconds to execute
[self doSomething];
//This loads some other view, my final view
[self.view addSubview: someOtherView]
but it seems that the loading view is never displayed. Instead the previous view stays there until the "someOtherView" gets added. I put trace logs and the code does seem to get executed, I even replaced [self doSomething] with a sleep(2), but the intermediate loading view is never displayed.
If I remove [self.view addSubview:someOtherView]; then after a few seconds...(after doSomething finishes executing) the load view is displayed since there is no view that is pushed on top of it, however this is obviously not the functionality I want.
Can explain this behavior? Is there something about the rendering cycle that I am misunderstanding because it doesn't seem like the view (on the screen at least) is instantly updated, even though I call a [self.view addSubview: loadController.view];
Would I need to create a separate thread?
In general, for changes in the UI to be made visible to the user, control must return to the main runLoop. You are only returning to the runLoop after taking the loading view down and replacing it with the other view. One strategy for dealing with this is to move the code that does the loading onto another thread. NSOperation and NSOperationQueue can be used for this.
An even simpler approach is to use performSelectorInBackground:withObject to do the processing. Once processing is complete the UI can be updated again to show the data. It is important to remember that the UI updates must be carried out on the main thread. Use performSelectorOnMainThread:withObject:waitUntilDone: to accomplish this from the loading thread.
This sounds like a lot of complication but it is really as simple as breaking your single method up into three separate methods as follows:
Display the loading view and start the background process - this is the button action method.
Do the background loading - called from the button action function with performSelectorInBackground:withObject.
Remove the loading view and update the display with the data - called from the background thread with performSelectorOnMainThread:withObject:waitUntilDone.
I created a subclass of UIView where I initialized how my loading-view should work and look like. (My view appeared and slided in from the bottom with an nice animation).
I then added code that handled whether the loading-view should be visible or not in a subclass of UIViewController.
I then let all my viewcontrollers be an subclass of my new viewcontrollerclass which made it possible for me to do:
[self showloadingMessage:#"loading..."];
in all my viewcontrollers...