I have encountered something like this before, where a button only responds to touches in part of its view. I found a image view with a transparent area was on a higher z-level, which obscured the touches where the overlap occurred. This was due to the button being added as a subview before the obscuring view was added.
In the case of an action sheet, I thought it would be at the highest z-level over anything else. Since the action sheet was just initialized and shown, there is no way that I can see that something is covering the button.
(For what it's worth, I just converted the app into a universal app, and am testing in both iOS 4.3 and 5.0. There is no cancel button in the action sheet on the iPad. This problem exists when simulating for iPhone for both iOS 4.3 and 5.0.)
I'm looking for other ideas as to what is causing the problem.
UPDATE
Here is the code to show the action sheet
- (void) shareButtonPressed {
SHKItem *item = [SHKItem larkspreeSearchResult:searchResult];
item.image = eventImageRaw;
// Get the ShareKit action sheet
SHKActionSheet *actionSheet = [SHKActionSheet actionSheetForItem:item];
// Display the action sheet
[actionSheet showInView:self.view];
}
Since this is for a subclassed action sheet, this, right out of ShareKit, might be helpful, too.
+ (SHKActionSheet *)actionSheetForType:(SHKShareType)type
{
SHKActionSheet *as = [[SHKActionSheet alloc] initWithTitle:SHKLocalizedString(#"Share")
delegate:self
cancelButtonTitle:nil
destructiveButtonTitle:nil
otherButtonTitles:nil];
as.item = [[[SHKItem alloc] init] autorelease];
as.item.shareType = type;
as.sharers = [NSMutableArray arrayWithCapacity:0];
NSArray *favoriteSharers = [SHK favoriteSharersForType:type];
// Add buttons for each favorite sharer
id class;
for(NSString *sharerId in favoriteSharers)
{
class = NSClassFromString(sharerId);
if ([class canShare])
{
[as addButtonWithTitle: [class sharerTitle] ];
[as.sharers addObject:sharerId];
}
}
// Add More button
[as addButtonWithTitle:SHKLocalizedString(#"More...")];
// Add Cancel button
[as addButtonWithTitle:SHKLocalizedString(#"Cancel")];
as.cancelButtonIndex = as.numberOfButtons -1;
return [as autorelease];
}
The showInView method has not been overridden.
If this view controller is part of a UINavigationController with a toolbar configured or a UITabBarController then any action sheet needs to be presented in the parent controller's view. For instance, [actionSheet showInView:self.navigationController.view]; or [actionSheet showInView:self.tabBarController.view];.
Related
Part of my iOS app is a file viewer with a UIActivityViewController. From it, I would like users to be able to do normal activity view stuff with the file, like mailing it, saving it to photos (if it's a photo), and everything else. However, my app has a bookmarks feature. I would like for users to be able to bookmark files from this menu. I have created a custom UIActivity to add the object to the bookmarks list, but I have not been able to figure out how to have it use the system bookmark icon. Is this even possible?
EDIT: For clarification, this is in the menu that you get when you click a "share" button.
Easy peasy. It all depends on how you are initiating it, is it in a navigation controller, swipe gesture, toolbar item or just a regular button? Either way it doesn't matter but if you want a specific answer you'll have to ask a specific question.
Here is an example how to accomplish your goal buy using a bookmark button that is connected in Storyboard interface, however the IBActions for addToFav and download aren't an interface and don't need to be:
-(IBAction) moreActions:(id)sender {
UIActionSheet *moreActions = [[UIActionSheet alloc] initWithTitle:nil delegate:self cancelButtonTitle:#"Cancel" destructiveButtonTitle:nil otherButtonTitles:#"Add To Favorites", #"Download For Offline View",#"Open Document In :", nil];
[moreActions showInView:self.view];
}
And the way you perform actions is by calling the method below:
-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
switch (buttonIndex) {
case 0: [self addToFav:self]; //See IBAction below
break;
case 1: [self download:self]; //See IBAction below
break;
case 2: {
//UIDocumentInteractionController:
NSURL *docURL = [NSURL URLWithString:#"filePathStringHere"];
documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:docURL];
[documentInteractionController setDelegate:self];
[documentInteractionController presentOpenInMenuFromRect:CGRectZero inView:self.view animated:YES];
}
break;
}
}
- (IBAction)download:(id)sender {
//enter your download code here
}
- (IBAction)addToFav:(id)sender {
//Code to add to your favorites
}
I'm fairly new to iOS, so please keep answers clear. I've been toying with encapsulating UIAlertController in another UIViewController to use it as a replacement for UIActionSheet as it's deprecated in iOS 8.
The idea would be a replacement for UIActionSheet that is backward and forward compatible, , call it ImmortalActionSheet for instance.
If the component is used in iOS 8 or greater, it can use UIAlertController, otherwise it would fall back to UIActionSheet. That would make it a backward and forward compatible action sheet to replace many action sheets around an application I'm working with. And yes I need to maintain backward compatibility.
I've prototyped this but for whatever reason, when I present ImmortalActionSheet, the UIAlertController view itself will always show up at the top left (0, 0). No matter if I change the center, it never moves.
-(void) loadView
{
UIView * child = nil;
if ( [UIAlertController class] )
{
UIAlertController * alertController = [UIAlertController alertControllerWithTitle:#"Make Choice!!" message:#"Choose one!" preferredStyle:UIAlertControllerStyleActionSheet];
for (UIAlertAction * action in actions ){
[alertController addAction:action];
}
child = alertController.view;
[self addChildViewController:alertController];
}
self.view = [[UIView alloc] init];
self.view.backgroundColor = [UIColor clearColor];
[self.view addSubview:child];
}
I am trying to load a UIImage View into a tabbar application with a global class, the image doesn't load the image the first time the rootviewcontroler is loaded, but it does show the alert. If you click on another tabbar item to load another view and come back to the first view the alert and image both show up correctly.
#import "globalNetworkCheck.h"
#import "CheckNetworkAvaliblity.h"
#import "ViewController.h"
#implementation globalNetworkCheck
static globalNetworkCheck *getNetworkStatus = nil;
static UIView *viewNetworkError = nil;
+(globalNetworkCheck *)getNetworkStatus
{
#synchronized(self)
{ if(getNetworkStatus==nil) {
// getNetworkStatus= [globalNetworkCheck new];
if ([CheckNetworkAvaliblity CheckNetwork])
{
{
[(UIView *)viewNetworkError removeFromSuperview];
viewNetworkError.backgroundColor=[UIColor clearColor];
viewNetworkError.hidden=YES;
[viewNetworkError removeFromSuperview];
[[viewNetworkError subviews]
makeObjectsPerformSelector:#selector(removeFromSuperview)];
NSLog(#"Success, Your network is available");
}
}
else
{
UIImageView *imgNoConnection = [[UIImageView alloc] initWithFrame:CGRectMake(0, 70, 320 , 449)];
viewNetworkError = [[UIView alloc]init];
[imgNoConnection setImage:[UIImage imageNamed:#"Noconnection.png"]];
viewNetworkError.frame = CGRectMake(0, 0, 320 , 449);
[viewNetworkError addSubview: imgNoConnection];
viewNetworkError.hidden = NO;
[[UIApplication sharedApplication].keyWindow.rootViewController.view addSubview:viewNetworkError];
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle: #"Connection Failed"
message: #"You are not connected to the internet. Please check your network settings and try again."
delegate: self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
viewNetworkError.hidden = NO;
NSLog(#"Sorry, Network connection is unavailable");
}}}
return getNetworkStatus;
}
#end
I am calling the class method on
-(void)viewWillAppear:(BOOL)animated {
globalNetworkCheck *obj=[globalNetworkCheck getNetworkStatus];
Remove this line:
[viewNetworkError addSubview:viewNetworkError];
Strange to me, how are you going to add viewNetworkError to viewNetworkError.
Edit
I think you mean
[self addSubview:viewNetworkError];
You are going to need somewhere to add the view. Right now this class method has no association on the view hierarchy. If you want to keep it a class method like this then you'll either need to add your error views to the window or the root view controllers view.
[[UIApplication sharedApplication].keyWindow.rootViewController.view addSubview:viewNetworkError];
or
[[UIApplication sharedApplication].keyWindow addSubview:viewNetworkError];
Without understanding the full view stack it is a bit hard to answer further. Alternatively with more rework you can put this responsibility on the AppDelegate rather than a class method like this. Create a view that your App delegate can add to the hierarchy as needed, create a public getter on your app delegate to check current network status, etc. Here is an example of how to great a HUD type view:
http://blog.mugunthkumar.com/coding/ios-code-tweetbot-like-alertpanels/
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 have a query that pulls the 10 nearest objects and displays the title of each in a button in a collection view. What I am trying to do is have an action that opens a new view controller displaying information of the object chosen from the collection view. What I need help with is connecting the chosen object in the buttons action. I am new to Xcode and could use some advice. This is the code defining the query:
int i = 0;
for (PFObject *object in objects) {
if (i >= [self.EventTitles count]) break;//to make sure we only write up to the max number of UILabels available in EventTitles
[(UIButton *)self.EventButtons[i] setTitle:[object objectForKey:#"name"] forState:UIControlStateNormal];
[(UIButton *)self.EventButtons[i] addTarget:self action:(social:) forControlEvents:UIControlEventTouchUpInside];
i++;
}
And then there is the code with the action
- (IBAction)social:(id)sender{
UIActionSheet *share = [[UIActionSheet alloc] initWithTitle:nil delegate:self cancelButtonTitle:#"Close" destructiveButtonTitle:nil otherButtonTitles:#"Call",#"Directions",#"Website",#"Checkin", nil];
[share showInView:self.view];
}
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
//Each button title we gave to our action sheet is given a tag starting with 0.
if (buttonIndex == 0) {
NSString* yourActualNumber = [NSString stringWithFormat:#"tel:%#",????????];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:yourActualNumber]];
NSLog(#"phone");
//Check Twitter accessibility and at least one account is setup.
}
I just do not know how to connect the object to the action, and then display that information on the new view controller.
UPDATE: I updated the code with the new line in the query, however it fails stating that the action social is undeclared however it is? I also changed the action to perform an action sheet rather than a new view controller. What I need now it a connection from the object to the action and how to write in the object for key appropriately in the action sheet function.
What I need help with is connecting the chosen object in the buttons
action.
[button addTarget:self action:#selector(DetailEvent1:) forControlEvents:UIControlEventTouchUpInside]
and then display that information on the new view controller.
I don't really know what you are asking here, if you clarify I'll try my best to help :)
Here is an example where an action sheet is displayed when an item is selected. I'm simply using the index path of the selected item, but I assume you have some other data you'd need to save so the Action Sheet delegate can use it.
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
NSString *str = [NSString stringWithFormat:#"Call number %d", indexPath.row];
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:nil delegate:(id < UIActionSheetDelegate >)self cancelButtonTitle:#"Cancel" destructiveButtonTitle:nil otherButtonTitles:str, nil];
[actionSheet showInView:self.view];
}