When the user presses my 'import' button, they must be able to type in a URL, which they can then 'ok' or 'cancel'.
How do I do this?
Obviously I could create a new view containing a text field and 2 buttons. But this seems like over coding.
One solution I found involves ' hacking ' a UITextField into a UIAlertView: http://iphone-dev-tips.alterplay.com/2009/12/username-and-password-uitextfields-in.html
(EDIT: Better -- http://www.iphonedevsdk.com/forum/iphone-sdk-development/1704-uitextfield-inside-uialertview.html#post10643 )
This looks really ugly. It is clearly a hack.
Can anyone provide a better solution (or even a better implementation of the same solution path)?
OK After a ton of digging, here is the result.
Firstly, putting in 'UITextField UIAlertView' into SO's search returns dozens of hits.
It turns out there is a method for doing this, Need new way to add a UITextField to a UIAlertView but it is private API :|
Pretty much every other solution involves hacking a UIAlertView, which is ugly:
http://junecloud.com/journal/code/displaying-a-password-or-text-entry-prompt-on-the-iphone.html
http://iphone-dev-tips.alterplay.com/2009/12/username-and-password-uitextfields-in.html
https://github.com/josecastillo/EGOTextFieldAlertView/
How to move the buttons in a UIAlertView to make room for an inserted UITextField?
^ MrGando's answer is neat
http://iphonedevelopment.blogspot.com/2009/02/alert-view-with-prompt.html
Getting text from UIAlertView
The only proper solution I found (ie coding from scratch from a UIView) is here: https://github.com/TomSwift/TSAlertView
This is all it takes:
- (IBAction) importTap
{
TSAlertView* av = [[[TSAlertView alloc] init] autorelease];
av.title = #"Enter URL";
av.message = #"";
[av addButtonWithTitle: #"Ok"];
[av addButtonWithTitle: #"Cancel"];
av.style = TSAlertViewStyleInput;
av.buttonLayout = TSAlertViewButtonLayoutNormal;
av.delegate = self;
av.inputTextField.text = #"http://";
[av show];
}
// after animation
- (void) alertView: (TSAlertView *) alertView
didDismissWithButtonIndex: (NSInteger) buttonIndex
{
// cancel
if( buttonIndex == 1 )
return;
LOG( #"Got: %#", alertView.inputTextField.text );
}
and Presto!
Check out: https://github.com/enormego/EGOTextFieldAlertView
I have created a post in my blog on the topic "How to add UITextField to UIAlertView from XIB". Using XIB to add is easier than pure coding. You can add whatever in a customized small view, then add the small view into the AlertView using 1 line code. Please refer to the following link.
http://creiapp.blogspot.com/2011/08/how-to-add-uitextfield-to-uialertview.html
Related
So I am implementing a custom navigation item in my view controller via the method like this
-(UINavigationItem*)navigationItem{
item = [[SearchNavigationItem alloc] init];
item.delegate = self;
return item;
}
The SearchNavigationItem will set itself up, add a UITextField and so on.
The field.delegate will have the item as the delegate.
So the issue I have is that when I try to grab the text of the field, it is nil. But when the "textfield changed" is called, I can access the field via the argument (textFieldDidChange:UITextField*) and it has the text.
Another issue, like the title, was that when I did [field resignFirstResponder] nothing happened.
Okay so I already have the answer, and I am writing this question because I could personally not find any help while fixing it.
So the issue is that navigationItem can be called multiple times and this kept creating new bars.
So the solution became, simply, this:
-(UINavigationItem*)navigationItem{
// Apparently it should be treated as a 'singleton' which I think it says
// kind of in the documentation. This comment is just to reinforce that
// it burned me to init it each time this method is called. Which is can
// be multiple times and also outside of the class itself (like when nav'ing)
if(item == nil){
item = [[SearchNavigationItem alloc] init];
item.delegate = self;
}
return item;
}
Hope this helps someone else.
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;
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.
So I am neck deep in a project, and we kept running into this problem in multiple places throughout it. When we display a UIAlertView the standard, typical way, the title doesn't display on the alert, only the description and buttons. We do the initialization and display as anyone would:
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Are you sure?" message:#"Deleting a ..." delegate:self cancelButtonTitle:#"Delete" otherButtonTitles:#"Cancel", nil];
[alert show];
Has anyone else ever run into this problem? We aren't importing and dependency that changes anything with that, nor are we doing anything that we can tell would cause this.
Edit: Here's an example of what I'm seeing:
I also met the same problem. The root cause is that I used "UIFont+Replacement" in my project. In iOS 7, system will use "UIFont fontWithDescriptor:" to setup font for labels in system controls.
I added a judgment to check whether the font replacement is used for system controls like UIAlertView. Please check my code, so far it works well:
UIFontDescriptor *replaceDescriptor = descriptor;
if (![descriptor.fontAttributes objectForKey#"NSCTFontUIUsageAttribute"]
|| ![((NSString *)[descriptor.fontAttributes objectForKey:#"NSCTFontUIUsageAttribute"])
isEqualToString:#"UICTFontUsageEmphasizeBody"])
{
NSString *replacementFontName = [replacementDictionary objectForKey:descriptor.postscriptName];
replaceDescriptor = [UIFontDescriptor fontDescriptorWithName:replacementFontName size:pointSize];
}
return [self replacement_fontWithDescriptor:replaceDescriptor size:pointSize];
I saw this tweet by Marco Armant:
Subclassed UIActionSheet w/target:action:userInfo: on buttons to avoid delegates/buttonIndex. Didn't someone else do this? Can't find it.
I think that sounds like a great idea, but I wasn't able to find anyone's code that did this. Does anyone know of one, before I go do it myself?
Yes, see my github for OHActionSheet.
It is implemented using blocks, so that you can use it this way, even without deporting the target/action code elsewere in your source code, the great advantage being that everything is located in the same place in your source code, and that you can use as many OHActionSheets as you want in the same controller
NSURL* anURL = ... // some URL (this is only as an example on using out-of-scope variables in blocks)
[OHActionSheet showSheetInView:yourView
title:#"Open this URL?"
cancelButtonTitle:#"Cancel"
destructiveButtonTitle:nil
otherButtonTitles:[NSArray arrayWithObjects:#"Open",#"Bookmark",nil]
completion:^(OHActionSheet* sheet,NSInteger buttonIndex) {
if (buttonIndex == sheet.cancelButtonIndex) {
NSLog(#"You cancelled");
} else {
NSLog(#"You choosed button %d",buttonIndex);
switch (buttonIndex-sheet.firstOtherButtonIndex) {
case 0: // Open
// here you can access the anURL variable even if this code is executed asynchrously, thanks to the magic of blocks!
[[UIApplication sharedApplication] openURL:anURL];
break;
case 1: // Bookmark
default:
// Here you can even embed another OHAlertView for example
[OHAlertView showAlertWithTitle:#"Wooops"
message:#"This feature is not available yet, sorry!"
cancelButton:#"Damn"
otherButtons:nil
onButtonTapped:nil]; // no need for a completion block here
break;
} // switch
}
}];
[EDIT] Edited sample code to add more details and usage examples