When you long press on an item in my app a UIMenuController pops up and gives me a few options. One of which is to delete.
Here is an example of what that looks like:
- (void)messageCellDidLongPress:(FCIMessageCell *)cell
{
self.cellShowingMenuController = cell;
[self becomeFirstResponder];
self.messageMenuController = [UIMenuController sharedMenuController];
self.messageMenuController.anchorView = cell.bodyLabel;
self.messageMenuController.presentInView = cell;
[self.messageMenuController updatePosition];
UIMenuItem *editMenuItem = [[UIMenuItem alloc] initWithTitle:#"Edit" action:#selector(edit:)];
UIMenuItem *unsendMenuItem = [[UIMenuItem alloc] initWithTitle:#"Unsend" action:#selector(unsend:)];
UIMenuItem *infoMenuItem = [[UIMenuItem alloc] initWithTitle:#"Details" action:#selector(info:)];
self.messageMenuController.menuItems = #[unsendMenuItem, editMenuItem, infoMenuItem];
[self.messageMenuController setMenuVisible:YES animated:YES];
//Delete button doesn't appear because its set up to appear on conditions
}
What I want to do: Once someone presses the delete button have a "CONFIRM" or "CANCEL" option appear, probably using a UIActionSheet. I'm not too sure how to do that. Where would I put the code to get it to appear. I'm not sure if I need to create an entire other view, or what?
In .h
#interface MyViewController : UIViewController <UIActionSheetDelegate>
In .m
-(IBAction) edit:(id)sender
{
self.editSheet = [[UIActionSheet alloc] initWithTitle:nil
delegate:self
cancelButtonTitle:#"cancel"
destructiveButtonTitle:#"delete"
otherButtonTitles:nil];
[self.editSheet showFromBarButtonItem:sender animated:YES];
}
and later on, in .m
- (void)actionSheet:(UIActionSheet *)actionSheet
clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (self.editSheet == actionSheet) {
if((actionSheet.cancelButtonIndex) == buttonIndex) {
// Cancel, do nothing
} // etc.
}
There is a bit more to it, like not creating a sheet if it already exists, possibly handling iPhone & iPad differently, etc.
Related
I have a method which throws up an actionSheet which is populated by an array.
The problem is that as soon as I place that method in the sender for 1 button, it fires off for every single button press.
I only want it to display for the one button (btnPortfolio).
Here is the code for my buttons:
- (IBAction)btnPortfolio:(id)sender {
[self populatePortfolioList];
}
- (IBAction)btnAdd:(id)sender {
}
- (IBAction)btnRefresh:(id)sender {
}
and here is my method:
-(void)populatePortfolioList{
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:#"Choose your option"
delegate:self
cancelButtonTitle:nil
destructiveButtonTitle:nil
otherButtonTitles:nil];
for (NSString *title in portfolio_list) {
[actionSheet addButtonWithTitle:title];
}
actionSheet.cancelButtonIndex = [actionSheet addButtonWithTitle:#"Cancel"];
[actionSheet showInView:self.view];
}
since you are using IBActions, I assume that you have somehow created the methods by ctrl+dragging from the buttons into your code? is that the case? it might be possible that you accidentally connected more than one of your buttons with the method btnPortfolio which causes populatePortfolioList to be called any time the button is pressed
It could be probably a bug on iOS7. But the last button is not separated from the previous one
As you can see from the image. This happens on both Simulator and device using iOS7 GM.
Does everyone else has the same problem?
UIActionSheet *actionSheet = [[UIActionSheet alloc]
initWithTitle:#"Title"
delegate:self
cancelButtonTitle:nil
destructiveButtonTitle:nil
otherButtonTitles:#"First", #"Second", #"Third", #"Fourth", nil];
[actionSheet showInView:self.view];
As you can see the code is quite simple.
Any idea on how to fix the problem? Or some third party library I can use instead of UIActionSheet ?
I think ActionSheet requires a cancel button.So you can add the cancel button title.
Another way is: Specify actionSheet's cancelButtonIndex.
For example,in your case, you can add a "Cancel" in otherButtonTitles at index 4 and then specifiy
actionSheet.cancelButtonIndex = 4.
I found a way to make it work on iPhone and iPad in the least hacky way:
Only init the UIActionSheet with a title
Add your buttons
Add a "CANCEL" button at last
set the CancelButtonIndex to that last index
I assume that the missing separator is caused by the cancel button not being recognized as a separate case when adding it first or through the init.
I found that adding a cancel button with an empty string after initialization works. The cancel button won't show up and the separator shows up.
[sheet addButtonWithTitle: #""];
[sheet setCancelButtonIndex: sheet.numberOfButtons - 1];
But this only works for iPad. On iPhone, an empty cancel button shows up, but I found a hacky workaround to make it work. In addition to the above, in willPresentActionSheet add this code in:
NSInteger offset = 55;
CGRect superFrame = actionSheet.superview.frame;
superFrame.origin.y += offset;
[actionSheet.superview setFrame: superFrame];
// hide underlay that gets shifted with the superview
[(UIView*)[[actionSheet.superview subviews] objectAtIndex: 0] removeFromSuperview];
// create new underlay
CGRect underlayFrame = CGRectMake(0, -offset, superFrame.size.width, superFrame.size.height);
UIView* underlay = [[UIView alloc] initWithFrame: underlayFrame];
underlay.alpha = 0.0f;
[underlay setBackgroundColor: [UIColor colorWithWhite: 0.0f alpha: 0.4f]];
[actionSheet.superview insertSubview: underlay atIndex: 0];
// simulate fade in
[UIView animateWithDuration: 0.3f animations:^{
underlay.alpha = 1.0f;
}];
This shifts down the sheet to hide the cancel button off the screen
The simplest fix is to pass #"" to the cancel button title instead of nil during allocation.
UIActionSheet *actionSheet = [[UIActionSheet alloc]
initWithTitle:#"Title"
delegate:self
cancelButtonTitle:#"" // change is here
destructiveButtonTitle:nil
otherButtonTitles:#"First", #"Second", #"Third", #"Fourth", nil];
[actionSheet showInView:self.view];
UIActionSheet *asAccounts = [[UIActionSheet alloc]
initWithTitle:Localized(#"select_an_account")
delegate:self
cancelButtonTitle:nil
destructiveButtonTitle:nil
otherButtonTitles: nil];
for (int i=0; i<[result count]; i++) {
ACAccount *acct = [result objectAtIndex:i];
[asAccounts addButtonWithTitle:[acct username]];
asAccounts.tag = i;
}
[asAccounts addButtonWithTitle:Localized(#"Cancel")];
asAccounts.cancelButtonIndex = result.count;
[asAccounts showInView:self.view];
If you have a cancel button, the last row will be shown. That is the temp fix I am using now. Do not know any solution if you do not want a cancel button to show
It seems that the initWithTitle:delegate:cancelButtonTitle:destructiveButtonTitle:otherButtonTitles: method is buggy and you shouldn't specify any cancel button here at all. Btw, it seems that a destructive button set in that method also won't work very well.
Instead of it you should:
provide a custom cancel button as the last button in the buttons array, eg: ["Action 1", "Action 2", "Close"] or sheet.addButtonWithTitle("Close")
manually set the cancel button index, e.g. sheet.cancelButtonIndex = 2
Then everything will work as expected. On the iPad the button will be automatically hidden and on the iPhone it will be styled and placed in the proper way.
- (void)willPresentActionSheet:(UIActionSheet *)actionSheet {
if ([UIDevice currentDevice].systemVersion.floatValue < 8.0f) {
UIView *separator = [[UIView alloc] initWithFrame:CGRectMake(8, 88, actionSheet.frame.size.width - 16, 0.5)];
separator.backgroundColor = [UIColor colorWithRed:219.0f/255 green:219.0f/255 blue:223.0f/255 alpha:1];
[actionSheet addSubview:separator];
}
}
Every button has height 44. My actionSheet doesn't have title. And I wanted to add separator between second and third buttons. That's why I use number 88.
I am trying to create an alertView from the appDelegate but I cant get it to work. The alertView will act as a disclaimer when the app launches for the first time. I cant get it to work..the introViewcontroller does not appear. what am I doing wrong? ps. i use storyboards except for the intoViewController which is a nib file.my root view controller is a "fisrtViewController" here is my code: Thank you..
int a;
and in didFinishLaunchingWithOptions
if ( a == 0) {
UIAlertView *disclaimer = [[UIAlertView alloc] initWithTitle:#"Read Before use" message:#"By using this app you agree to its terms and conditions.\n\n\n\n\n\n\n\n\n\n\ntext heren\n\n\n\n\n\n\n\n\n\n\n\n\n" delegate:self cancelButtonTitle:#"No!" otherButtonTitles:#"Yes Let me In", nil];
[disclaimer show];
}
// Override point for customization after application launch.
return YES;
}
-(void) alertView:(UIAlertView *) alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if (buttonIndex == 1) {
// FirstViewController *firstView = [[FirstViewController alloc] init];
a+=1;
// [self.viewController presentModalViewController:firstView animated:YES];
}
else if (buttonIndex == 2) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Sorry!" message:#"You are not allowed to use this app due to the fact that you did not agree to the terms and Conditions. Please exit this app!" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alert show];
if (buttonIndex ==1) {
introViewController *intro = [[introViewController alloc] initWithNibName:#"introViewController" bundle:nil];
[self.viewController presentModalViewController:intro animated:YES];
introViewController *intro = [[introViewController alloc] initWithNibName:#"introViewController" bundle:nil];
[self.viewController presentModalViewController:intro animated:YES];
}
}
ahh... Now I Understand what you are trying to do.
You problem is that self.viewController is not set before you try to present a new modal ViewController. Also, I think your buttonIndex is of by one.
Try instead:
if (buttonIndex == 1)
{
_window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
introViewController *intro = [[introViewController alloc] initWithNibName:#"introViewController" bundle:nil];
_window.rootViewController = _intro;
[_window makeKeyAndVisible];
}
Since your problem has nothing to to do with the UIAlertView, You might want to change the title and description of your question to make it more clear, for others struggling with a similar problem.
Happy coding! :)
The best way to handle this use a some NSString value write to NSUserDefaults file. when the app first time launch with the accepting the disclaimer (eg:#"accept") if user not accepting also update that NSString value accordingly.(#"notAccept").
In didFinishLaunchingWithOptions
if (![[[NSUserDefaults standardUserDefaults]valueForKey:#"key_disclaimer"] isEqualToString:#"accepted"]) {
UIAlertView *disclaimer = [[UIAlertView alloc] initWithTitle:#"Read Before use" message:#"By using this app you agree to its terms and conditions.\n\n\n\n\n\n\n\n\n\n\ntext heren\n\n\n\n\n\n\n\n\n\n\n\n\n" delegate:self cancelButtonTitle:#"No!" otherButtonTitles:#"Yes Let me In", nil];
[disclaimer show];
[disclaimer release];
// make sure to release it.
}
//if user accept the disclaimer.
[[NSUserDefaults standardUserDefaults]setValue:#"accepted" forKey:#"key_disclaimer"];
//if user not accept the disclaimer,
[[NSUserDefaults standardUserDefaults]setValue:#"notAccepted" forKey:#"key_disclaimer"];
update "key_disclaimer" accordingly. thats the best way. in your example the value of a using in the temporally. if you close and restart the app it will ask again the disclaimer.
in your code
int a;
replace it by using
int a = 0;
it will be ok.
thanx.
If your code is exactly like you wrote above, a won't be 0.
If you don't explicitly set it to 0 at startup, it might have any value.
If you want to check if the app was opened the first time after installing, use NSUserDefaults
e.g. (in didFinishWithOptions):
int startCount=[[NSUserDefaults standardUserDefaults] intForKey:#"startCount"];
if (startCount==0]){
//Is the first start, show your agreement
//Then increase startCount so that this will not be called at next start
[[NSUserDefaults standardUserDefaults] setInteger:startCount++ forKey:#"startCount"];
}
You could also store your version number in the NSUserDefaults. This way, you could check if the user first starts a new version.
Is there a way to achieve this menu easily with the sdk or do I have to make it manually (combining an overlay view and other view with the buttons)?
Thanks in advance!
That's UIActionSheet class from standard UIKit framework
Easy example from Apple's documentation of UIActionSheet extended a bit by me to fire call action.
Delcare in the global scope of the ViewController:
UIActionSheet * actionSheet; and UIView * yourView;
Int the viewDidLoad:
actionSheet = [[UIActionSheet alloc] initWithTitle:nil
delegate:self
cancelButtonTitle:#"Cancel"
destructiveButtonTitle:#"Delete Note"
otherButtonTitles:#"Call",#"Add a Contact",nil];
[yourView = self.view]
To fire the menu by some declared button with IBAction you will need:
-(IBAction)viewMapButton:(id) sender
{
[actionSheet showInView:yourView];
}
To take appropriate action depending on user choice declare following method and check what [actionSheet buttonTitleAtIndex:buttonIndex] was equal to:
- (void)actionSheet:(UIActionSheet *)actionSheet
clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSString * what_action = [actionSheet buttonTitleAtIndex:buttonIndex];
NSLog(#"The %# button was tapped.", what_action);
if ([what_action isEqualToString:#"Call"])
{
NSString *phoneNumber = [[NSString alloc]
initWithString:#"telprompt:1234567890"];
[[UIApplication sharedApplication]
openURL:[NSURL URLWithString:phoneNumber]];
}
}
[NOTE: Firing of call does not work on iOS Simulator]
the UIActionSheet crashes after it appears the second time through..
.H file
..UIActionSheetDelegate>{
UIActionSheet *popupQuery;
}
#property (nonatomic, retain) UIActionSheet *popupQuery;
.M file
-(IBAction)showActionSheet:(id)sender {
if (popupQuery.visible) {
NSLog(#"popupQuery isVisible");
[popupQuery dismissWithClickedButtonIndex:-1 animated:YES];
return;
}else{
popupQuery = [[UIActionSheet alloc] initWithTitle:#"Title" delegate:self cancelButtonTitle:#"Cancel" destructiveButtonTitle:#"Readibility" otherButtonTitles:#"Email URL", #"Print", nil];
popupQuery.actionSheetStyle = UIActionSheetStyleBlackOpaque;
//[popupQuery showInView:self.view];
[popupQuery showFromBarButtonItem:actionButton animated:YES];
[popupQuery release];
}
}
When you release popupQuery after showing it from actionButton, you relinquish ownership on the object. If the object gets deallocated then popupQuery will point to a deallocated object which when you do popupQuery.visible might give you a crash. Since you have it as a property, you can do this –
self.popupQuery = [[[UIActionSheet alloc] initWithTitle:#"Title" delegate:self cancelButtonTitle:#"Cancel" destructiveButtonTitle:#"Readibility" otherButtonTitles:#"Email URL", #"Print", nil] autorelease];
Remove the release statement at the end. Now the object will be valid when you popupQuery.visible or self.poupQuery.visible In this case, you will have the ownership and you can safely access the object.
From the documentation for UIActionSheet:
- (void)dismissWithClickedButtonIndex:(NSInteger)buttonIndex animated:(BOOL)animated
Parameters
buttonIndex
The index of the button that was clicked. Button indices start at 0.
Therefore the problem may be your -1.