On iphone 3.0 how to disable Cut, Copy and Paste option - iphone

Does any one know, how can i disable cut, copy and paste option on iPhone 3.0?
Thanks for your help and time.

I, too, couldn't find much documentation on using canPerformAction:withSender: for this purpose. So, I settled for clearing the pasteboard when exiting the application. In my AppDelegate.m:
- (void)applicationWillTerminate:(UIApplication *)application {
NSLog(#"application terminating");
// Clear pasteboard to prevent pasting into other applications:
UIPasteboard *pasteBoard = [UIPasteboard generalPasteboard];
pasteBoard.items = nil;
}
This worked well for my user-annotated reference application. I don't mind users copying and pasting within my application, but I'd rather they not republish my original content.
At some point I'd like more fine-grained control, perhaps with canPerformAction:withSender:, so that I can allow users to copy/paste the content they do create themselves.

Override this method in the controller class.
// Hide cut/copy/paste menu
-(BOOL)canPerformAction:(SEL)action withSender:(id)sender {
if ( [UIMenuController sharedMenuController] )
{
[UIMenuController sharedMenuController].menuVisible = NO;
}
return NO;
}

Any responder (UIView or UIWindow subclass) can override the canPerformAction:withSender: method, so you could just return NO for all the actions you don't want to permit.
See the UIResponder documentation...

Related

UIWebView: Disable copy/cut options for a rich text editor

I have a UIWebView with a contentEditable div in order to implement some kind of rich text editor. I need to trimm the copy & cut options in the UIMenuController that appears in the web view once the user selects any piece of text.
There seems to be a lot of solutions around the web, but for some reason, non of them applies for my scenario.
I've subclassed the UIWebView and implemented the canPerformAction:(SEL)action withSender: to remove the copy and cut, but once the user chooses "Select" or "Select All", a new menu appears, and apparently, the web view does not intercept this action and the canPerform method is not being called.
Is there a way to trimm actions for this cases?
I will adapt another answer of mine for your case.
The canPerformAction: is actually called on the internal UIWebDocumentView instead of the UIWebView, which you cannot normally subclass. With some runtime magic, it's possible.
We create a class which has one method:
#interface _SwizzleHelper : UIView #end
#implementation _SwizzleHelper
-(BOOL)canPerformAction:(SEL)action
{
//Your logic here
return NO;
}
#end
Once you have a web view which you want to control the actions of, you iterate its scroll view's subviews and take the UIWebDocumentView class. We then dynamically make the superclass of the class we created above to be the subview's class (UIWebDocumentView - but we cannot say that upfront because this is private API), and replace the subview's class to our class.
#import "objc/runtime.h"
-(void)__subclassDocumentView
{
UIView* subview;
for (UIView* view in self.scrollView.subviews) {
if([[view.class description] hasPrefix:#"UIWeb"])
subview = view;
}
if(subview == nil) return; //Should not stop here
NSString* name = [NSString stringWithFormat:#"%#_SwizzleHelper", subview.class.superclass];
Class newClass = NSClassFromString(name);
if(newClass == nil)
{
newClass = objc_allocateClassPair(subview.class, [name cStringUsingEncoding:NSASCIIStringEncoding], 0);
if(!newClass) return;
Method method = class_getInstanceMethod([_SwizzleHelper class], #selector(canPerformAction:));
class_addMethod(newClass, #selector(canPerformAction:), method_getImplementation(method), method_getTypeEncoding(method));
objc_registerClassPair(newClass);
}
object_setClass(subview, newClass);
}

Adding Speech to custom UIMenuController

I created a custom UIMenuController in a UIWebView but it seems to get rid of the "Speak Selection" option in the UIMenuController after that. The speak selection option is turned on in Preferences on all test devices and it appears in other apps, including non-Apple apps. Is there an accessibility service or part of the sharedMenuController that I can call to get this item?
UIMenuItem *copyMenuItem = [[UIMenuItem alloc] initWithTitle:NSLocalizedString(#"Copy", #"Copy menu item") action:#selector(myappCopy:)];
UIMenuItem *highlightMenuItem = [[UIMenuItem alloc] initWithTitle:NSLocalizedString(#"Highlight", #"Highlight menu option") action:#selector(myappHighlight:)];
UIMenuItem *unhighlightMenuItem = [[UIMenuItem alloc] initWithTitle:NSLocalizedString(#"Remove Highlight", #"Remove Highlight menu option")
action:#selector(myappRemoveHighlight:)];
UIMenuItem *noteMenuItem = [[UIMenuItem alloc] initWithTitle:NSLocalizedString(#"Note", #"Note menu options") action:#selector(myappNote:)];
[UIMenuController sharedMenuController].menuItems = [NSArray arrayWithObjects:copyMenuItem, highlightMenuItem, unhighlightMenuItem, noteMenuItem, nil];
[copyMenuItem release];
[highlightMenuItem release];
[unhighlightMenuItem release];
[noteMenuItem release];
I even tried to parse the existing shared menu items at the start, but I don't see anything dumped in the log. The method is getting called on app launch.
Tried this at top of method:
for (UIMenuItem *menuItem in [UIMenuController sharedMenuController].menuItems) {
NSLog(#"title: %#", menuItem.title);
NSLog(#"action: %#", menuItem.action);
}
Any help is much appreciated! Thanks - Eric
I've found some interesting things with this bug. Basically, when speak selection is enabled, after you make the first selection the UIMenuController is emptied of menuItems. The solution, though hacky, is simple.
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
NSString *selectorString = NSStringFromSelector(action);
BOOL isAccessibilitySelector = [selectorString isEqualToString:#"_accessibilitySpeak:"] || [selectorString isEqualToString:#"_accessibilityPauseSpeaking:"];
if (isAccessibilitySelector && [super canPerformAction:action withSender:sender]) {
//(re)add menuItems to UIMenuController
return YES;
}
return NO;
}
I should note that you must re-add the menuItems after their canPerformAction...() has been called.
I have submitted this as radar:12931434. Update: DUP'ed to 13060693.
Some of the UIMenuController items can be found in UIResponder.h in UIKit framework.
#interface NSObject(UIResponderStandardEditActions) // these methods are not implemented in NSObject
- (void)cut:(id)sender __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_0);
- (void)copy:(id)sender __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_0);
- (void)paste:(id)sender __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_0);
- (void)select:(id)sender __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_0);
- (void)selectAll:(id)sender __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_0);
- (void)delete:(id)sender __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_2);
- (void)makeTextWritingDirectionLeftToRight:(id)sender __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_5_0);
- (void)makeTextWritingDirectionRightToLeft:(id)sender __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_5_0);
#end
But there is no speak text option there. It turns out if you override "canPerformAction: WithSelector:", within a subclass of your UIWebView or UITextField as listed below, you will also get a listing of all of the actions sent to self including the UIMenuController options.
// Override
- (BOOL) canPerformAction:(SEL)action withSender:(id)sender
{
NSLog(#"%#",NSStringFromSelector(action));
//if you are customizing your menu, return NO except for your specific selectors
return YES;
}
You'll find several methods that may interest you, including _accessibilitySpeak: and _accessibilityPauseSpeaking: and _define: (please note these three selectors are iOS 5 only). The underscore means that they are private, so also keep in mind that you can't directly call them with the classic [class selector] syntax.
Remember, these are system menuItems, which means Apple will stick them in front of any menu items you add, often leaving your menu items in a second layer accessed by tapping the > arrow. If you want to control the order in which the items are display, and/or mix Apple's system items with your items, you will need to create custom menu items for these actions that call a method in your class like this:
- (void) myAppSpeak: (UIMenuController*) sender
{
[super performSelector:#selector(_accessibilitySpeak:)];
}
Keep in mind that these methods need to be implemented in a subclass of a class that implements these already, such as a sub class of UIWebView....not a subclass of UIWebViewController.
Then inside the controller, or wherever you build your UIMenuController, create the custom button that calls this method. Be sure if you are in a web view, you are referencing an object of type of your subclass, and not the generic webview. Otherwise, it won't work.
UIMenuItem *speakMenuItem = [[UIMenuItem alloc] initWithTitle:#"Speak" action:#selector(myAppSpeak:)];
[UIMenuController sharedMenuController].menuItems = [NSArray arrayWithObjects:speakMenuItem, etc. etc., nil];
Even though you are adding it to the your menu items, it will not appear unless you return YES for the selector in your canPerformAction: WithSelector: in your subclass of your web view or text field. So feel free to add items here that may be circumstantial otherwise. You can use logic in your subclassed view to sort that out.

UIWebView without Copy/Paste and selection rectangle when showing documents

In my App I want to disable the Copy/Paste/Cut of contents that are showed by a UIWebView object. To achieve this, I created a UIWebView subclass and overrode the - (BOOL)canPerformAction:(SEL)action withSender:(id)sender method:
#pragma mark - UIResponderStandardEditActions
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
if (action == #selector(copy:) ||
action == #selector(paste:)||
action == #selector(cut:)) {
return _copyCutAndPasteEnabled;
}
return [super canPerformAction:action withSender:sender];
}
Now the user no longer can make such operations, however the UIWebView still shows the "selection rectangle", as you can see in the following screenshot:
NOTE: The content being showed in the UIWebView is not HTML pages. I'm showing document files (PDF, DOC, PPT), loaded from a file using:
NSURL *fileURL = [NSURL fileURLWithPath:<document file path..>];
NSURLRequest *fileRequest = [NSURLRequest requestWithURL:fileURL];
[<uiwebView> loadRequest:fileRequest];
Is there any way to disable/hide this selection rectangle feature too?
[]s,
You can try injecting javascript into the webView. This code works on the iPhone too but only when the page is fully loaded.
http://www.javascriptsource.com/page-details/disable-text-selection.html or
http://solidlystated.com/scripting/proper-way-to-disable-text-selection-and-highlighting/
To get it to work properly when the page is only half loaded or still loading you'll proably have to use a setup similar to this one where you inject the disabling javascript just as it would start selecting.
http://www.icab.de/blog/2010/07/11/customize-the-contextual-menu-of-uiwebview/
One last thing I can think of that might work, have you tested adding the selector #selector(select:) to the list of disabled selectors?
EDIT:
Ok as you what to display a PDF rather then a html page you can try this.
Setup
Put the webView in a scrollView;
Set the ScrollView's: delegate to self; min zoom to 1.0 and max zoom to whatever you want;
After the weView has finished loading.
Scan for the first UIScrollView in the webView, (we are scanning for it so this shouldn't break on later iOS versions).
Set the UIWebViews frame to it's scrollViews size.
And set the scrollView's contentSize to the webViews contentSize (or by now, it's frame).
-(void)setup{
//Get the webView's first scrollView (the one all the content is in).
UIScrollView *webViewContentView;
for (UIView *checkView in [webView subviews] ) {
if ([checkView isKindOfClass:[UIScrollView class]]) {
webViewContentView = (UIScrollView*)checkView;
break;
}
}
webView.frame = CGRectMake(0, 0, webViewContentView.contentSize.width, webViewContentView.contentSize.height);
scrollView.contentSize = webViewContentView.contentSize;
scrollView.delegate = self;
}
To enable zooming you'll need to add an extra method from the scrollViews delegate.
-(UIView *) viewForZoomingInScrollView:(UIScrollView *)scrollView {
return webView;
}
And you can download a sample project http://www.multiupload.com/P8SOZ4NW6C (It's an xcode 4 project).
NOTES: The zooming pixelates the webView's content if you zoom in to much because the webView doesn't know to re-render things, and of cause you can't click links (but in a PDF that shouldn't be a problem).
EDIT 2: Better method
Since writing this I have realised a MUCH simpler and easier method, which should also solve the pixelation problem.
-(void)removeInput{
UIScrollView *webViewContentView;
for (UIView *checkView in [webView subviews] ) {
if ([checkView isKindOfClass:[UIScrollView class]]) {
webViewContentView = (UIScrollView*)checkView;
break;
}
}
for (UIView *checkView in [webViewContentView subviews] ) {
checkView.userInteractionEnabled = NO;
}
}
Now you should only be able to interact with the UIScrollView itself, meaning zooming, scrolling, ect.. should still work (and the page should re-render rather the pixelating when you zoom) but you can't select any text, or type anything in.
Also something about this method is that based on this list of UIWebView subViews it might be possible to set this before the page starts loading rather then after it has finished loading the page.
I found an answer on here by Johnny Rockex and it worked like a champ. It works for documents (including .pdf files) displayed in a UIWebView. UIWebView without Copy/Paste when displaying PDF files

iPhone: How to detect if an EKEvent instance can be modified?

While working with the EventKit on iPhone I noticed that some events can exist which cannot be modified. Examples I encountered so far are birthdays and events synced with CalDAV. When you view the event's details in the standard built-in calendar app on iPhone the "Edit" button in the top-right corner is not visible in these cases, where it would be visible when viewing "normal" events.
I've searched everywhere, read all documentation there is but I simply can't find anything that tells me how to detect this behavior! I can only detect it afterwards:
edit an event's title
save it to the event store
check the event's title, if it has not changed it is not editable!
I am looking for a way that I can detect the non-editable behavior of an event beforehand. I know this is possible because I've seen other calendar apps implement this correctly.
Ok it appears as if the SDK doesn't provide me with anything I can use to check if an EKEvent is read-only. I created a workaround by creating a category that adds an "isReadOnly" method to all EKEvent instances.
EKEvent+ReadOnlyCheck.h
#interface EKEvent(ReadOnlyCheck)
- (BOOL) isReadOnly;
#end`
EKEvent+ReadOnlyCheck.m
#import "EKEvent+ReadOnlyCheck.h"
#implementation EKEvent(ReadOnlyCheck)
- (BOOL) isReadOnly {
BOOL readOnly;
NSString *originalTitle = [self.title retain];
NSString *someRandomTitle = [NSString stringWithFormat:#"%i", arc4random()];
self.title = someRandomTitle;
readOnly = [originalTitle isEqualToString:self.title];
self.title = originalTitle;
[originalTitle release];
return readOnly;
}
#end
When the above files are in place I can simply call isReadOnly on the EKEvent of my choice.
#import "EKEvent+ReadOnlyCheck.h"
...
if ([event isReadOnly]) {
// Do your thing
}
...
I haven't worked with Event Kit yet, but from the documentation it seems that editability is a property of a calendar, not of an event. event.calendar gets you the event's calendar, and calendar.allowsContentModifications tells you if the calendar is read-only or read-write.
try allowsEditing property of EKEventViewController before displaying the view.
Yes. It is possible. The code would look like following :
Try relating to code by logging the output of objects I use with editable/non editable events and you will understand the working:)
EKEventViewController *controller = [[EKEventViewController alloc] init];
controller.event = myEvent; /*myEvent is of type EKEvent*/
if(controller.navigationItem.leftBarButtonItem != NULL)
{
/*Event is Editable, Your code here*/
}
I think adding this category method for EKEvent handles all cases where events are not editable:
- (BOOL)isReadOnly {
if (self.calendar.allowsContentModifications == NO) return YES;
if (self.organizer && [self.organizer isCurrentUser] == NO) return YES;
return NO;
}

Can I hook into UISearchBar's Clear Button?

I've got a UISearchBar in my interface and I want to customise the behaviour of the the small clear button that appears in the search bar after some text has been entered (it's a small grey circle with a cross in it, appears on the right side of the search field).
Basically, I want it to not only clear the text of the search bar (which is the default implementation) but to also clear some other stuff from my interface, but calling one of my own methods.
I can't find anything in the docs for the UISearchBar class or the UISearchBarDelegate protocol - it doesn't look like you can directly get access to this behaviour.
The one thing I did note was that the docs explained that the delegate method:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText;
is called after the clear button is tapped.
I initially wrote some code in that method that checked the search bar's text property, and if it was empty, then it had been cleared and to do all my other stuff.
Two problems which this though:
Firstly, for some reason I cannot fathom, even though I tell the search bar to resignFirstResponder at the end of my method, something, somewhere is setting it back to becomeFirstResponder. Really annoying...
Secondly, if the user doesn't use the clear button, and simply deletes the text in the bar using the delete button on the keyboard, this method is fired off and their search results go away. Not good.
Any advice or pointers in the right direction would be great!
Thanks!
Found the better solution for this problem :)
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{
if ([searchText length] == 0) {
[self performSelector:#selector(hideKeyboardWithSearchBar:) withObject:searchBar afterDelay:0];
}
}
- (void)hideKeyboardWithSearchBar:(UISearchBar *)searchBar{
[searchBar resignFirstResponder];
}
The answer which was accepted is incorrect. This can be done, I just figured it out and posted it in another question:
UISearchbar clearButton forces the keyboard to appear
Best
I've got this code in my app. Difference is that I don't support 'live search', but instead start searching when the user touches the search button on the keyboard:
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
if ([searchBar.text isEqualToString:#""]) {
//Clear stuff here
}
}
Swift version handling close keyboard on clear button click :
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
if searchText.characters.count == 0 {
performSelector("hideKeyboardWithSearchBar:", withObject:searchBar, afterDelay:0)
}
}
func hideKeyboardWithSearchBar(bar:UISearchBar) {
bar.resignFirstResponder()
}
You could try this:
- (void)viewDidLoad
{
[super viewDidLoad];
for (UIView *view in searchBar.subviews){
for (UITextField *tf in view.subviews) {
if ([tf isKindOfClass: [UITextField class]]) {
tf.delegate = self;
break;
}
}
}
}
- (BOOL)textFieldShouldClear:(UITextField *)textField {
// your code
return YES;
}
I would suggest using the rightView and rightViewMode methods of UITextField to create your own clear button that uses the same image. I'm assuming of course that UISearchBar will let you access the UITextField within it. I think it will.
Be aware of this from the iPhone OS Reference Library:
If an overlay view overlaps the clear button, however, the clear button always takes precedence in receiving events. By default, the right overlay view does overlap the clear button.
So you'll probably also need to disable the original clear button.
Since this comes up first, and far as I can see the question wasn't really adequately addressed, I thought I'd post my solution.
1) You need to get a reference to the textField inside the searchBar
2) You need to catch that textField's clear when it fires.
This is pretty simple. Here's one way.
a) Make sure you make your class a , since you will be using the delegate method of the textField inside the searchBar.
b) Also, connect your searchBar to an Outlet in your class. I just called mine searchBar.
c) from viewDidLoad you want to get ahold of the textField inside the searchBar. I did it like this.
UITextField *textField = [self.searchBar valueForKey:#"_searchField"];
if (textField) {
textField.delegate = self;
textField.tag = 1000;
}
Notice, I assigned a tag to that textField so that I can grab it again, and I made it a textField delegate. You could have created a property and assigned this textField to that property to grab it later, but I used a tag.
From here you just need to call the delegate method:
-(BOOL)textFieldShouldClear:(UITextField *)textField {
if (textField.tag == 1000) {
// do something
return YES;
}
return NO;
}
That's it. Since you are referring to a private valueForKey I can't guarantee that it will not get you into trouble.
Best solution from my experience is just to put a UIButton (with clear background and no text) above the system clear button and than connect an IBAction
- (IBAction)searchCancelButtonPressed:(id)sender {
[self.searchBar resignFirstResponder];
self.searchBar.text = #"";
// some of my stuff
self.model.fastSearchText = nil;
[self.model fetchData];
[self reloadTableViewAnimated:NO];
}
Wasn't able to find a solution here that didn't use a private API or wasn't upgrade proof incase Apple changes the view structure of the UISearchBar. Here is what I wrote that works:
- (void)viewDidLoad {
[super viewDidLoad];
UITextField* textfield = [self findTextFieldInside:self.searchBar];
[textfield setDelegate:self];
}
- (UITextField*)findTextFieldInside:(id)mainView {
for (id view in [mainView subviews]) {
if ([view isKindOfClass:[UITextField class]]) {
return view;
}
id subview = [self findTextFieldInside:view];
if (subview != nil) {
return subview;
}
}
return nil;
}
Then implement the UITextFieldDelegate protocol into your class and overwrite the textFieldShouldClear: method.
- (BOOL)textFieldShouldClear:(UITextField*)textField {
// Put your code in here.
return YES;
}
Edit: Setting the delegate on the textfield of a search bar in iOS8 will produce a crash. However it looks like the searchBar:textDidChange: method will get called on iOS8 on clear.