Completely disable the swipe back gesture throughout the entire app - ios

I want to disable the swipe back gesture that allows users to go back.
I've tried:
if ([self.navigationController respondsToSelector:#selector(interactivePopGestureRecognizer)]) {
self.navigationController.interactivePopGestureRecognizer.enabled = NO;
}
and all sorts of other code I've found online, but none of them work at all? I'm using iOS 8.3.
Is there a way to disable this all together? Thank you.

First of all as #Fogmeister said in comments, you need to have a very good reason to remove this native function to your app.
Now, having said this, the solution:
SwipeBack and JRSwizzle (Needed by SwipeBack)
You can use this in a single ViewController in which you want to remove the functionality or you create a custom class for UINavigationController and you use it there:
#import <SwipeBack/SwipeBack.h>
- (void)viewWillAppear:(BOOL)animated
{
//For a single viewcontroller
self.navigationController.swipeBackEnabled = NO;
//If you are in the custom class
self.swipeBackEnabled = NO;
}
Hope it helps.

Related

'Shortcut' for actions on objects

So I have 3 objects in my project right now:
_vLabel.hidden = YES;
_wLabel.hidden = YES;
_nextButton.hidden = YES;
As you can see I do the .hidden thing for all of them . Is there a way of creating variable or something with which I would only have to apply it instead writing the whole line, like:
_vLabel.hide;
Thanks in advance!
It seems silly to do it, as you should just follow the convention that any other iOS programmer expects and understands, but yes you can do it:
UIView+MyCategory.h:
#interface UIView (MyCategory)
-(void) hide;
#end
UIView+MyCategory.m:
#implementation UIView (MyCategory)
-(void) hide {
[self setHidden:YES];
}
#end
usage:
[myView hide];
I find the question unclear. What would be a "shortcut" in this situation? How can you get any "shorter" than this:
_vLabel.hidden = YES;
_wLabel.hidden = YES;
_nextButton.hidden = YES;
To me, the "length" here - the thing that needs shortening - is that you're doing something three times instead of once. If these objects are objects that you typically hide and show together, then it would be nice to have one method that hides and shows all of them:
- (void) toggle {
_vLabel.hidden = !_vLabel.hidden;
_wLabel.hidden = !_wLabel.hidden;
_nextButton.hidden = !_nextButton.hidden;
}
It's just as much code, but once you've written it, then every time you want to show them all or hide them all you just say:
self.toggle;
Which is itself actually a shortcut for:
[self toggle];
So what you'd be doing here is writing a method in order to change what the language a little, and that's a perfectly reasonable thing to do if it avoids repetition.
Hmm, if your only problem is that writing label.hidden = YES; is too long then why donĀ“t you just use code snippets? That way you keep using the naming conventions with less writing.
You can even use Xcode to create these snippets. Just select the text and drag it from the edge(this part can be pretty tricky) to the snippets area.
Personally though I would use something like TextExpander or aText or whatever else you prefer to do this. These apps are usually faster than Xcode and easier to use.
Just create in those apps snippet for .hidden = YES; and assign it to a shortcut like .hide
So now whenever you want to hide something you just write myLabel.hide TextExpander will expand it into
myLabel.hidden = YES;
And to make things visible again you could assign .show to .hidden = NO;

How to programmatically make text selectable and provide popup menus to go to another app

I am trying to implement a feature in iOS project that when you select a piece of text and highlight it you can then choose from the menu options to use another app like the default dictionary. Is it possible to do this? If so where can I find such documentation or tutorials?
You are describing the iOS menu. Look at the documentation on classes such as UIMenu, UIMenuItem, and UIMenuController.
I've found a solution to my problem.
Thanks to the author of this article:
http://blog.studiovillegas.com/2014/02/06/ios-uipasteboard-uimenucontroller-and-uimenuitem/
To add a custom menu item on to the default menu controller.
ViewController.h
- (void)longPressGestureRecognizer:(UIGestureRecognizer *)recognizer
{
UIMenuItem *mi = [self.label menuItemOpenPleco];
UIMenuController *menuController = [UIMenuController sharedMenuController];
menuController.menuItems = #[mi];
}
PasteboardLabel {h,m}
#interface PasteboardLabel : UILabel
- (UIMenuItem *)menuItemOpenPleco;
#end
#implementation PasteboardLabel
- (UIMenuItem *)menuItemOpenPleco
{
return [[UIMenuItem alloc] initWithTitle:#"Open Pleco" action:#selector(openPleco:)];
}
- (void)openPleco:(id)sender
{
NSString *selectedText = [self textInRange:[self selectedTextRange]];
UIPasteboard *pb = [UIPasteboard generalPasteboard];
pb.string = selectedText;
NSString *urlString = [NSString stringWithFormat:#"plecoapi://x-callback-url/q?s=%#", pb.string];
NSURL *url = [[NSURL alloc] initWithString:urlString];
[[UIApplication sharedApplication] openURL:url];
}
#end
I've found that there's a dearth of examples of adding custom menu items, or explanations of how they work. So I wanted to resolve that by sharing a few important tidbits then showing an example.
The UIMenuController "talks" with UIViews, not with UIViewControllers. This means that your UIMenuController related code needs to go into subclasses of UIView rather than a UIViewController.
Notice the word The at the start of my prior example. There's only one UIMenuController, a singleton which is shared from when your application first starts until it ends. This means that you should only add your item once, and that you shouldn't be writing over the existing array of items.
The appearance of the button in the UIMenu is based on whether or not the UIView that was tapped responds to the selector. This means you need to implement the method if you want the button to appear, and that you don't need to worry about it appearing when unrelated views are tapped unless you pick a selector name for which other UIViews also have methods.
So, having said all that, I made a subclass of a UITextView (which means its a subclass of UIView per my first bullet) and then I gave it this initialize method, along with an implementation for my selector.
+ (void)initialize {
static dispatch_once_t addInsert;
dispatch_once(&addInsert, ^{
UIMenuController *mController = [UIMenuController sharedMenuController];
UIMenuItem *insert = [[UIMenuItem alloc] initWithTitle:#"Insert..."
action:#selector(insert:)];
mController.menuItems = [mController.menuItems arrayByAddingObject:insert];
});
}
- (void)insert:(id)sender {
NSLog(#"Insert... pressed!");
}
The important points above here:
It's in the class initialize method, which is called by the runtime before the first time any other method in your class is invoked. In practice means the code is handled just before the first time an instance of your custom view will be appearing on screen.
I added a dispatch_once guard around it. If my class is subclassed, it's possible that those subclasses will call this initialize method. Maybe those subclasses show up before this one does, so I don't want to prevent the initialize method from running then. I just want to prevent it from running multiple times. Thus why I wrapped the code in a dispatch_once.
I didn't just set the menuItems to a new array of items - I assigned it to a new array of items that extended the existing array of items with my new item.
Hope you find all of that helpful. It's not very complicated, and you can certainly go about implementing my second point in other ways - I tried to pick a way that seemed safest to me, but there are certainly simpler ways of doing it.

UIActivity activityViewController not dismissing on iPad

I have a UIActivity subclass that creates its own activityViewController:
- (UIViewController *)activityViewController {
WSLInProgressViewController* progressView = [[[WSLInProgressViewController alloc] init] autorelease];
progressView.message = [NSString stringWithFormat:NSLocalizedString(#"Posting to %#...",#"Posting to..."),
self.activityType];
return progressView;
}
I've add a full repro on GitHub.
According to the documentation, you aren't supposed to dismiss this manually. Instead, the OS does that when you call activityDidFinish:. This works fine when ran on an iPhone.
When I say "works," this is the sequence of events that I'm expecting (and see on the iPhone):
Display the UIActivityViewController
User presses my custom activity
My view controller appears
I call activityDidFinish:
My custom view controller is dismissed
The UIActivityViewController is also dismissed
However, when I run this same code on the iPad Simulator -- the only difference being that I put the UIActivityViewController in a popup, as the documentation says you should -- the activityViewController never dismisses.
As I say, this is code wo/the popUP works on the iPhone and I have stepped through the code so I know that activityDidFinish: is getting called.
I found this Radar talking about the same problem in iOS6 beta 3, but it seems such fundamental functionality that I suspect a bug in my code rather than OS (also note that it works correctly with the Twitter and Facebook functionality!).
Am I missing something? Do I need to do something special in the activityViewController when it's run in a UIPopoverViewController? Is the "flow" supposed to be different on the iPad?
The automatic dismissal only appears to happen when your 'activity' controller is directly presented, not wrapped in anything. So just before showing the popup it's wrapped in, add a completion handler
activity.completionHandler = ^(NSString *activityType, BOOL completed){
[self.popup dismissPopoverAnimated:YES];
};
and you'll be good.
I see the question is quite old, but we've been debugging the same view-controller-not-dismissing issue here and I hope my answer will provide some additional details and a better solution than calling up -dismissPopoverAnimated: manually.
The documentation on the UIActivity is quite sparse and while it hints on the way an implementation should be structured, the question shows it's not so obvious as it could be.
The first thing you should notice is the documentation states you should not be dismissing the view controller manually in anyway. This actually holds true.
What the documentation doesn't say, and what comes as an observable thing when you come across debugging the non-dissmissing-view-controller issue, is iOS will call your -activityViewController method when it needs a reference to the subject view controller. As it turns out, probably only on iPad, iOS doesn't actually store the returned view controller instance anywhere in it's structures and then, when it wants to dismiss the view controller, it merely asks your -activityViewController for the object and then dismisses it. The view controller instantiated in the first call to the method (when it was shown) is thus never dismissed. Ouch. This is the cause of the issue.
How do we properly fix this?
Skimming the UIActivity docs further one may stumble accross the -prepareWithActivityItems: method. The particular hint lies along the following text:
If the implementation of your service requires displaying additional UI to the user, you can use this method to prepare your view controller object and make it available from the activityViewController method.
So, the idea is to instantiate your view controller in the -prepareWithActivityItems: method and tackle it into an instance variable. Then merely return the same instance from your -activityViewController method.
Given this, the view controller will be properly hidden after you call the -activityDidFinish: method w/o any further manual intervention.
Bingo.
NB! Digging this a bit further, the -prepareWithActivityItems: should not instantiate a new view controller each time it's called. If you have previously created one, you should merely re-use it. In our case it happily crashed if we didn't.
I hope this helps someone. :)
I had the same problem. It solved for me saving activityViewController as member and return stored controller. Activity return new object and dismiss invoked on new one.
- (UIViewController *)activityViewController {
if (!self.detaisController) {
// create detailsController
}
return self.detailsController;
}
I pass through the UIActivity to another view then call the following...
[myActivity activityDidFinish:YES];
This works on my device as well as in the simulator. Make sure you're not overriding the activityDidFinish method in your UIActivity .m file as I was doing previously. You can see the code i'm using here.
a workaround is to ask the calling ViewController to perform segue to your destination ViewController via - (void)performActivity although Apple does not recommend to do so.
For example:
- (void)performActivity
{
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
[self.delegate performSomething]; // (delegate is the calling VC)
[self activityDidFinish: YES];
}
}
- (UIViewController *)activityViewController
{
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
{
UIViewController* vc=XXX;
return vc;
}
else
{
return nil;
}
}
Do you use storyboards? Maybe in your iPad storyboard, the UIActivityIndicatorView doesn't have a check on "Hides When Stopped"?
Hope it helps!
So I had the same problem, I had a custom UIActivity with a custom activityViewController and when it was presented modally it would not dismiss not matter what I tried. The work around I choose to go with so that the experience remained the same to the user was to still use a custom UIActivity but give that activity a delegate. So in my UIActiviy subclass I have the following:
- (void)performActivity
{
if ([self.delegate respondsToSelector:#selector(showViewController)]) {
[self.delegate showViewController];
}
[self activityDidFinish:YES];
}
- (UIViewController *)activityViewController
{
return nil;
}
Then I make the view controller that shows the UIActivityViewController the delegate and it shows the view controller that you would otherwise show in activityViewController in the delegate method.
what about releasing at the end? Using non-arc project!
[progressView release];
Many Users have the same problem as u do! Another solution is:
UIActivityIndicatorView *progress= [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(125, 50, 30, 30)];
progress.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhiteLarge;
[alert addSubview:progress];
[progress startAnimating];
If you are using storyboard be sure that when u click on the activityind. "Hides When Stopped" is clicked!
Hope that helped...

Disable action items in UIDocumentInteractionController's presentPreviewAnimated

I am using UIDocumentInteractionController's presentPreviewAnimated method to preview document. It works fine. But I wish to disable the action button while in the preview mode. I have the following two delegated methods to return NO. But these two methods never got called at all. The other delegated methods work fine. Any suggestion?
-(BOOL)documentInteractionController:(UIDocumentInteractionController *)controller canPerformAction:(SEL)action {
NSLog(#"canPerformAction");
return NO;
}
and
-(BOOL)documentInteractionController:(UIDocumentInteractionController *)controller performAction:(SEL)action {
NSLog(#"performAction");
return NO;
}
I finally switched to use the QLPreviewController class. Where you can subclass it and make the action button gone away. (With the help from the answer by rbrown of this SO.

Using the same View Controller for add, display, and edit

I am working on an iOS app that uses a very common Core Data based tableview to display items and when one it selected, it shows a more detailed view, much like the Contacts app. The detail view itself is a programmatically generated grouped table with a custom (nib-defined) view for a header that has a picture and a name. Some of the cells in the table are custom cells that have a label name and a textbox value. In "edit" mode, the editable table cells (and the name in the header) have .clearButtonMode set to UITextFieldViewModeAlways to show that they are editable.
I am currently using the same view controller to display the detailed information, edit the information, and add a new record to the original list.
When a new item is being added, the view controller is created modally with a custom init overload that sets a flag in the view controller to indicate that it is adding the record. This allows it to start in edit mode and if edit mode is left, the model view is dropped away. The right menubar button is the usual Edit/Done, and the left one is a cancel button. When an existing item is being edited, the left button (normal back button) is replaced with a cancel button.
I am starting to have second thoughts as to whether or not having one view controller handle three different modes is the way to go. There are few issues that I am not sure how to handle.
1) How do I tell if edit mode is left by hitting "Done"? Is there an action for it? If cancel is hit, the action either dismisses itself (add mode) or restores the previous values leaves edit mode. I suppose I could put a check in my setEditing override to handle it, but it seems that there should be a better way.
2) When edit mode is entered and I set the editable text fields to UITextFieldViewModeAlways, is there a way to animate the appearance of the 'X' buttons so that they fade in with the editing indicators on the regular cells?
Are there easy solutions to these problems or is my 3-in-1 view controller a bad idea? It doesn't seem right to remake the same view for different modes, but having multiple modes for a view controller seems to be a bit of a hassle.
jorj
I like the 3-in-1 approach and use it all the time. There are lots of advantages: one xib, one view controller, one simple protocol between the list and detail view controllers. Yes, there are a few more checks like if (self.editing) ... but I like that better than more view controllers and xibs.
To help with adding I expose a BOOL that the delegate can set.
#property (nonatomic) BOOL adding;
1) The built-in editButtonItem does not allow you to intercept it before setEditing:animated: This is problematic when you are doing data validation after Done is tapped. For that reason I rarely use editButtonItem and use my own Edit, Done, and Cancel buttons with their own action methods. See below.
2) For this I like UITableView's reloadSections:withRowAnimation. It might work in your case.
- (void)edit:(id)sender
{
self.editing = YES;
}
- (void)done:(id)sender
{
// data validation here
if (everythingChecksOut)
{
//save here
} else {
return; //something didn't validate
}
//if control reaches here all is good
//let the delegate know what happened...
if (self.adding) {
[self.delegate didFinishAddingWithData:self.yourData];
} else {
[self.delegate didFinishEditingWithData:self.yourData];
}
self.adding = NO;
self.editing = NO;
}
- (void)cancel:(id)sender
{
[self.view endEditing:YES]; //in theory, forces the view that is editing to resign first responder
//in practise I find it doesn't work with the YES parameter and I have to use my own flag
// roll back any changes here
self.editing = NO;
if (self.adding) //let the delegate know we cancelled the add...
{
[self.delegate didCancelAdd];
}
}
- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{
[super setEditing:editing animated:animated];
//set your nav bar title
[self.tableview.editing = editing]; //you may or may not require this
[self.tableview reloadSections... withRowAnimation:yourChoice];
if (editing)
{
//install your Done and Cancel buttons
} else {
//remove Cancel and put the Edit button back
}
}
Then in viewDidLoad...
- (void)viewDidLoad
{
[super viewDidLoad];
//whatever else you do
if (self.adding)
{
self.editing = YES;
}
}
I haven't fully understood the questions you have raised, but here are some thoughts on structure which are probably more useful in the first instance...
It seems you are doing too much with a single UITableViewController and inevitably you will end up with lots of if statements and confusing code. I'd break it down into two separate UITableViewControllers, one to handle the main view (and any subsequent editing mode you require) and then another to handle the detail view. Either or both of these could then use nibs as you require.
Using two controllers like this will allow you to simply push the second detailViewController onto a navigation stack, rather than presenting it modally which doesn't seem like the obvious thing to do in this instance.
However, if you would prefer it to be presented modally, you could write a protocol for the detailView which sends messages in the event of 'Cancel', 'Edit' or 'Done' buttons being pushed. The first viewController could then implement the protocol and receive these events.
I hope that helps...

Resources