I would like to put the undocumented trash can animation in my program. The call
method is:
+ (void)animateToolbarItemIndex:(unsigned)index duration:(double)duration target:(id)target didFinishSelector:(SEL)selector;
Can anyone figure out what I should plug in for:
index
duration
target
selector
?
My trials are not working resulting in the error:
2011-11-15 16:05:20.639 CNiPhone[973:707] +[UIToolbar animateToolbarItemIndex:duration:target:didFinishSelector:]: unrecognized selector sent to class 0x3f019c08
2011-11-15 16:05:20.641 CNiPhone[973:707] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[UIToolbar animateToolbarItemIndex:duration:target:didFinishSelector:]: unrecognized selector sent to class 0x3f019c08'
Here is the relevant code:
#interface UIToolbar (privateMethods2)
+ (void)animateToolbarItemIndex:(unsigned)index duration:(double)duration target:(id)target didFinishSelector:(SEL)selector;
#end
[UIToolbar animateToolbarItemIndex:0 duration:0.5 target:trashToolbarButton didFinishSelector:#selector(animateTrashStep2)];
[UIToolbar commitAnimations];
- (void) animateTrashStep2 {
}
You dont need to do any undocumented stuff for this, just create a custom UIButton. Download the UIKit Artwork Extractor and you'll find the frames for the trash can animation, as well as the UIBarButtonItem background.
You need to call it on the toolbar connected to your IBOutlet as opposed to the class. E.g.:
[self.myToolbar /*(possibly just myToolbar)*/ animateToolbarItemIndex:0 duration:0.5 target:trashToolbarButton didFinishSelector:#selector(animateTrashStep2)];
You will probably find the answer here: https://stackoverflow.com/a/5101910/796103
It's probably because your target is set to trashButtonItem. The target is the object that the didFinishSelector will be sent to. Try setting the target to self. Also, according to http://iphonedevwiki.net/index.php/UIToolbar this is not a class method so you will need to replace the [UIToolbar with the actual toolbar object.
In your didFinishSelector callback I guess you call the method again and the trashcan will close.
Good luck.
This 'undocumented' method is documented here: http://iphonedevwiki.net/index.php/UIToolbar
It's documented as being an instance method and not a class method, which explains the error message you're receiving in the exception.
#jrtc27 has answered the question correctly, in that this should be sent to the UIToolbar instance instead. From your reply to the comments, it seems that you have not changed your class category to assist the compiler. Try the following instead:
#interface UIToolbar (privateMethods2)
- (void)animateToolbarItemIndex:(unsigned)index duration:(double)duration target:(id)target didFinishSelector:(SEL)selector;
#end
And then use:
[self.navigationController.toolbar animateToolbarItemIndex:0 duration:0 target:self.trashToolbarButton didFinishSelector:#selector(animateTrashStep2)];
I think if you know how to do the suckEffect, then you can do a little bit hacking with the toolbar.
Basically, all the official controls are a subclass of UIView, hence you can find out the view hierarchy of the UIToolBar instance.
If you don't know how to find out the subview hierarchy of a given view, you can use a PRIVATE API - (void)recursiveDescription from UIView. Remember to use it in DEBUG configuration.
Why should we bother the view hierarchy?
The answer is, to hide certain view, or to add a subview as we want.
What's next
Find the origin UIBarButtonItem view of your trash can
Before the suckEffect start, hide it, add a new trash can view which can do the animation of open/close/shaking. This moment I think you need to ask it to do open animation.
Then let the suckEffect fly...
After the suckEffect ended, ask your view to do the close animation.
After the close animation is finished, remove your view, and reshow the original trash can view.
Possibility?
I haven't done it before, but I think it's a possible solution because creating an trash can view with open/close/shaking animation is easy.
Risk?
Anyway, this solution is like some kind of hacking without touching the private api, the risk is on your own.
Good luck.
Related
I'm still quite new to Xcode etc, so Im a bit fuzzy when it comes do creating/using objects in Xcode.
For example, I used a tutorial for creating a GestureRecognizer in code - it's pretty simple.
Right now I have only this in my .h:
#interface PlayPage : UIViewController <UIGestureRecognizerDelegate>
And this in my .m:
UITapGestureRecognizer *tapTwo=[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(doubleTap)];
[tapTwo setDelegate:self];
[tapTwo setNumberOfTapsRequired:2];
[tapTwo setNumberOfTouchesRequired:1];
[self.view addGestureRecognizer:tapTwo]; //adds it to view
Here are my question(s):
1) If I also want to see the GestureRecognizer in IB along with the above code (perhaps so I can change some settings since I'm not familiar with everything) how do I accomplish that?
2) How would you decide when to use all code vs. a mix of code/IB ? Is it strictly a matter of style/comfort/familiarity?
3) It seems declaring different objects requires different 'setup' code (UITextLabel vs GestureRecognizer vs UITextField,for example) - how do you know when to use which code?
Thanks for helping to clear this up.
In Xcode 4.x Interface builder there are UIGestureRecognizer objects that you can drag onto your views. So you would need to add the UIGestureRecognizer in Interface builder (if you need to do any more config in code then connect them up with an outlet as well).
I try and do anything I can in IB and drop to code when I have to but it is a matter of preference.
I'd rather eyeball the position of views and use the snap guides than spend a while figuring it out in code.
You can hide a lot of boiler plate code if you do it in IB
Start typing and look at the autocompletion options to jog your memory. But more importantly every time you use a class you have not used before you should at least skim the documentation to see what is suggested. Half of the questions on this site could easily be solved if the question poster actually bothered to look at the vast documentation available.
Update
You need to create the UIGestureRecognizer in Interface Builder not your code.
In Interface Builder ensure that the Utilities pain is visible and drag and drop a UIGestureRecognizer of the required class onto your view
This is what the UIGestureRecognizer class look like:
You then configure the object in the Utilities panel. If you want to be able to access this object in your code then you will need an IBOutlet.
In your classes header file add a new ivar for this
#property (nonatomic, retain) IBOutlet UITapGestureRecognizer *tapGestureRecognizer;
In the implementation file synthesize this
#synthesize tapGestureRecognizer = _tapGestureRecognizer;
Then back in Interface builder connect this up by ctrl + click + dragging from the File's Owner to the UIGestureRecognizer subclass. Then selecting your ivar from the window that appears:
probably a very simple question but can't find the right answer anywhere. I am using XCode 4 and working on an iphone app, which probably sums up all the info that I need to provide.
Here it is:
- I created a ViewBasedApplication
- At some point depending on the user input, I load a TableView
But now how on Earth do I add a button or something to return? Note: I can't use a NavigationBased app, that would be easier but would not work for me.
Help anyone?
If you used a UITableViewController, you may want to use a UIViewController instead. In the UIVeiwController, you can add a UITableView along with your own UINavigationBar or, if you don't want to use a UINavigationBar, you could leave room for some type of custom UIButton. Either the UINavigationBar button or your custom UIButton action could trigger a close of your UIViewController.
If you add the UIViewController as a subview, then Cyprian's [self removeFromSuperView]; would work. If you present as a modal as Jamie suggests, you could use [self dismissModalViewControllerAnimated:YES];.
Well I don't know you code but you could always call
[self removeFromSuperView];
I’m trying to implement a partial overlay modal in my app with the code from “Semi-Modal (Transparent) Dialogs on the iPhone” at ramin.firoozye.com. The overlay functionality works and it slides the modal into view, but calling any IBAction from the modal's controller causes an “Unrecognized Selector Sent to Instance” crash.
I recreated the basic functionality with that code isolated, and it triggers the same error. To see what I’m talking about, you can download the test project here.
I’m sure I’m just missing something simple here. Any help would be greatly appreciated.
When showing your ModalViewController in TestViewController displayModal:, you release your modalController (line 20). Don't do this - you need the ViewController to stay alive. If you release it, only the view keeps alive (as it is retained when added as a subview).
Also, in ModalViewController hideModalEnded you release modalView, which you didn't retain, so I'd remove that one as well.
So now you need to release just the instance of ModalViewController after the view got removed. You can do this by [self release]; in hideModalEnded, but this seems to be an unusual pattern and I don't feel good doing it.
Some suggestions:
Keep the show and hide methods in the
same class.
Keep an ivar around with the
controller.
Another possiblity: Remove the
ModalViewController altogether and
put everything in TestViewController - But this very much depends on how much action there will be going on in the real thing.
So in my app delegate I add a call add the myViewController.view to the main window:
1. [window addSubview:myViewController.view];
In myViewController I do the following code in the viewDidAppear method:
2. [self presentModalViewController: yourViewController animated: YES];
In my yourViewController class I do the following to try and go back to the main window
3. [self dismissModalViewControllerAnimated:YES];
My main windows view appears with buttons in all, but the buttons won't react to any click or anything. It's like there is something over them that I can't see.
Also, the main windows button works before this process but doesn't after the process.
Any help would be appreciated.
If the dismiss method call is in the modal view controller (not the parent that presents it), then you actually want to call [self.parentController dismissModalViewControllerAnimated:YES];
There are a number of reasons why things might not be responding to your touches. Here are two that have happened to me:
The frame of the view you want to touch is too small. UIViews can draw outside of their frames, so it might look ok, but not respond if the touch is technically outside of the frame -- you also have to check that all the superview's up the hierarchy also have a large enough frame.
If anything in your view is a UIImageView or child thereof, it won't respond to user touches because UIImageView has userInteractionEnabled set to NO by default. You can fix this just by setting myImageView.userInteractionEnabled = YES;
Edit: Oli pointed out in the comments that dismissModalViewControllerAnimated: should work if called on either self.parentController or simply self, since that method is smart enough to call the parent if needed, according to the docs. The docs also make it sound like this might behave differently if you have multiple model views open at once, though, so I would still consider it cleaner code to call the method on self.parentController directly.
I have a UIViewController that is initialised with a correct frame, however somewhere in my code the frame gets mangled and I'm having difficulty finding out where.
In situations like this it is usually handy to watch a variable in the debugger, however I have no way of accessing the controller->view->frame property in my variable view, since it isn't a variable, it's a property (surprisingly enough)
Drilling into the UIView in the variables display shows a few things but nothing I can relate to the frame, I thought perhaps that would be in layer but it isn't.
Is there any way to watch for changes in a private API? I guess not, since the variables are essentially 'hidden' and so you can't specify exactly what to watch.
Alternatively, what other approach could I use? I already tried subclassing UIView, setting my UIViewController's view to point to this subclass and breaking on the setFrame method but it didn't seem to work.
EDIT: the subclassing UIView method DID work, I just had to set the view to point to my test subclass in viewDidLoad and not the init method. Leaving this question open as I'm not sure if this is the best way of approaching this kind of problem...
Subclass your the view you want to track and rewrite the setFrame method:
#implementation MyTableView
- (void)setFrame:(CGRect)frame;
{
NSLog(#"%#", frame);
[super setFrame:frame];
}
#end
Then use the debugger to add a breakpoint to it and check when it gets called. Eventually, you'll see when the frame gets changed and where does the change comes from.
I discovered this can be done using key value observers.
http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/KeyValueObserving/KeyValueObserving.html
You could create an ivar, view2, and just assigned it to your view in your loadView method. That should enable you to watch it like a normal variable.