Anyone is getting this message while trying to show UIActionSheet from popover?
Your application has presented a UIAlertController () of style UIAlertControllerStyleActionSheet. The modalPresentationStyle of a UIAlertController with this style is UIModalPresentationPopover. You must provide location information for this popover through the alert controller's popoverPresentationController. You must provide either a sourceView and sourceRect or a barButtonItem. If this information is not known when you present the alert controller, you may provide it in the UIPopoverPresentationControllerDelegate method -prepareForPopoverPresentation.
Previously to the GM I used some workaround for converting the UIActionSheet to UIAlertController and this is working fine.
However it seems that Apple tried to solve the UIActionSheet issues and I didn't want to use my workaround - but it seems that I have no choice...
To support iPad, include this code:
alertView.popoverPresentationController?.sourceView = self.view
alertView.popoverPresentationController?.sourceRect = self.view.bounds
// this is the center of the screen currently but it can be any point in the view
self.presentViewController(alertView, animated: true, completion: nil)
If you are presenting the action sheet after the user makes a selection on a cell within a UITableView. I found that this works decently well:
UIAlertController *alert = [UIAlertController alertControllerWithTitle:#"Directions"
message:#"Select mode of transportation:"
preferredStyle:UIAlertControllerStyleActionSheet];
alert.popoverPresentationController.sourceView = cell;
alert.popoverPresentationController.sourceRect = cell.bounds;
UIAlertAction *defaultAction = [UIAlertAction actionWithTitle:#"Cancel" style:UIAlertActionStyleCancel handler:nil];
//...
[self presentViewController:alert animated:YES completion:nil];
You need to provide popoverPresentationController for iPad support. In this, you either specify barButtonItem or sourceView. This another thread may help you: Swift UIAlertController - ActionSheet iPad iOS8 Crashes
Actually it is something buggy (I believe) in Xcode for iPhone and iPad designs for now.
In iPhone same code works perfect and you can see the alert message at same position (always). But for iPad you need to define the alert box's position with alert.popoverPresentationController.sourceView = self.view;
alert.popoverPresentationController.sourceRect = CGRectMake(self.view.bounds.size.width / 2.0 - 105, self.view.bounds.size.height / 2.0 + 70, 1.0, 1.0); 105 and 70 are the approximate dimension differences for iPad portrait design due to different anchor point.
In iPhone design UIAlertController comes with 'Modal View' but unfortunately if you use same code for iPad it will not be a 'Modal View'. Which means that you need to write extra code for disabling touches in iPad design. I think it is weird.
In iPad design you need to consider that anchor point is different. It is the bubble triangle point, not the upper left of AlertView.
These are the weird things I see. I think that there must be a standard and if someone wants to go out standards, fine, there can be other options.
UIAlertController being iOS8 only, and needing to support iOS7, I am using it. I ran into this on a Master view in a Master/Detail layout on iPad. I was able to work around it (not exactly fix it) by raising the UIActionSheet from the parent UISplitViewController using [actionSheet showInView:]. Good luck.
If you want to present it in the centre with no arrows on iPads and normally on iPhones: [Swift 3+]:
if let popoverController = alertController.popoverPresentationController {
popoverController.sourceView = self.view
popoverController.sourceRect = CGRect(x: self.view.bounds.midX, y: self.view.bounds.midY, width: 0, height: 0)
popoverController.permittedArrowDirections = []
}
self.present(alertController, animated: true, completion: nil)
Related
I'm working in a custom keyboard (iOS App Extension). I have a UICollectionView in my Keyboard Layout, so when one item is selected I want to show a message (UIAlerView for example).
Here is my code:
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
...
UIAlertController * alert= [UIAlertController
alertControllerWithTitle:#"My Title"
message:#"Enter User Credentials"
preferredStyle:UIAlertControllerStyleAlert];
[self presentViewController:alert animated:YES completion:nil];
}
I get this error: 'Feature not available in extensions of type com.apple.keyboard-service'
So...is there any way to show a message from an App Extension?
EDIT:
Here is an example. The IKEA Emoticons Keyboard shows a message (like an Android Toast after selecting one item).
I also have tried this library:
iOS Toast Library
Sad to say that, but there's no way to show a UIAlertView in keyboard extension. Actually, nothing above the frame of InputViewController can be showed. It's pretty clear in the Apple's doc:
...a custom keyboard can draw only within the primary view of its UIInputViewController object... it is not possible to display key artwork above the top edge of a custom keyboard’s primary view, as the system keyboard does on iPhone when you tap and hold a key in the top row.
As for message inside the keyboard, there are some useful libraries that can help us with it. For example https://github.com/scalessec/Toast and https://github.com/sergeylenkov/PTHintController.
Finally I solved the problem. It's not the best solution, but at least I get the effect that I wanted.
I've created a View simulating a Toast in the xib file and set it to hidden.
When the item is selected, I show the "faked" Toast for 2 seconds and hide it again.
self.popUpView.hidden = NO;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
self.popUpView.hidden = YES;
});
I don't know if it's a good solution, but I really had to find a solution for that.
For Swift you can use this :
self.popUpView.isHidden = false
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
self.popUpView.isHidden = true
}
I have some very standard code for UIActionSheet that works well on production.
-(void) showGameMenu
{
UIActionSheet * const activeSheet = [[UIActionSheet alloc] initWithTitle:#"Game"
delegate:self
cancelButtonTitle:#"Cancel"
destructiveButtonTitle:nil
otherButtonTitles:#"New Game", #"Save Game", #"Load Game", #"Email Game", #"Share Position", #"Setup Position", nil];
[activeSheet setTag:Framework::GameMenuButton];
[self showActionSheetOnToolbar:activeSheet];
}
-(void) showActionSheetOnToolbar:(UIActionSheet *)sheet
{
[sheet showFromBarButtonItem:_gameItem animated:YES];
}
_gameItem is not nil and is a proper UIBarButtonItem for the "Game" item as shown in the screenshot. The code has been working well on production until recently I tried to compile the same project with iOS 8 (it was compiled with iOS 7).
Suddenly, the menu got cut-off around the bottom edge of the device. Furthermore, there was a transparent black-thing covered about 3/4 of the screen horizontally.
I have no clue why this happens and not sure how to fix it.
Are you using auto-layout?
I am not sure but it can be about constraints.I say this because your "Game" and "Flip" buttons look smaller than what you expected.
So I recommend you to delete constraints on your view to your main view and add them again without checking "constrain to margins" via "pin" menu from bottom.
If this is not the case please add what you see in iOS 7.
I have the following code for a UIPopoverController. It is working fine in iOS 7. However, in iOS 8, the popover becomes full-screen which I do not want. How do I keep the popover from filling the entire screen in iOS8?
CGRect buttonFrame = [[[[[self tabBarController] tabBar] subviews] objectAtIndex:index+1] frame];
popover = [[UIPopoverController alloc]initWithContentViewController:viewmapmenu] ;
popover.popoverContentSize = CGSizeMake(95, 128.0) ;
popover.delegate = self ;
[popover presentPopoverFromRect:buttonFrame inView:self.tabBarController.tabBar permittedArrowDirections:UIPopoverArrowDirectionDown animated:YES] ;
Try to add this delegate method.
- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller;{
return UIModalPresentationNone;
}
See, here is the situation. if you are presenting the popover in iPhone then it will not work in iOS 8 as Apple has completely restricted it even if it was previously working. Now you cannot present popover in iPhone as all the access to the private methods of UIPopover is blocked.
You can look into the FPopover Library in github to use popover in iPhone:-
https://github.com/50pixels/FPPopover
Adding some controls to UIAlertView was deprecated in iOS7 using addSubview method. As I know Apple promised to add contentView property.
iOS 7 is released now and I see that this property is not added. That is why I search for some custom solution with ability to add progress bar to this alertView. Something for example similar to TSAlertView, but more ready for using in iOS 7.
Here is a project on Github to add any UIView to an UIAlertView-looking dialog on iOS7.
(Copied from this StackOverflow thread.)
It took me only 1 day to create my own alert view that looks exactly like Apple's
Take a screenshot of Apple's alert for reference (font sizes, spacings, width)
Create a xib with title, message, custom view and tables for buttons (Apple uses tables instead of UIButton now, default table cell is good enough). Note you need 3 button tables: two for left and right buttons (whenever the number of buttons is 2), another one for the other cases (one button or more than 2 buttons).
Implement all the methods from UIAlertView on your custom alert.
Show/Dismiss - you can create a specific modal window for your alerts but I just put my alerts on top of my root view controller. Register your visible alerts to a static array. If showing the first alert/dismissing the last, change tint mode of your window/view controller to dimmed/to automatic and add/remove a dimming view (black with alpha = 0.2).
Blurred background - use Apple's sample code (I used opaque white)
3D dynamic effects - use Apple's sample code (5 lines of code). If you want a nice effect, take a slightly bigger snapshot in step 5 and add inverse animators for alert background and foreground.
EDIT:
Both blurred background and the paralax effect sample code can be found in "iOS_RunningWithASnap" WWDC 2013 sample code
Paralax effect:
UIInterpolatingMotionEffect* xAxis = [[[UIInterpolatingMotionEffect alloc] initWithKeyPath:#"center.x"
type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis] autorelease];
xAxis.minimumRelativeValue = [NSNumber numberWithFloat:-10.0];
xAxis.maximumRelativeValue = [NSNumber numberWithFloat:10.0];
UIInterpolatingMotionEffect* yAxis = [[[UIInterpolatingMotionEffect alloc] initWithKeyPath:#"center.y"
type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis] autorelease];
yAxis.minimumRelativeValue = [NSNumber numberWithFloat:-10.0];
yAxis.maximumRelativeValue = [NSNumber numberWithFloat:10.0];
UIMotionEffectGroup *group = [[[UIMotionEffectGroup alloc] init] autorelease];
group.motionEffects = #[xAxis, yAxis];
[self addMotionEffect:group];
The blurred background is the only complicated thing. If you can use an opaque color instead, use it. Otherwise it's a lot of experimenting. Also note that blurred background is not a good solution when the background is dark.
For the show/dismiss animationg, I am using the new spring animation method:
void (^animations)() = ^{
self.alpha = 1.0f;
self.transform = CGAffineTransformIdentity;
};
self.alpha = 0.0f;
self.transform = CGAffineTransformMakeScale(0.5f, 0.5f);
[UIView animateWithDuration:0.3
delay:0.0
usingSpringWithDamping:0.7f
initialSpringVelocity:0.0f
options:UIViewAnimationOptionCurveLinear
animations:animations
completion:^(BOOL completed) {
//calling UIAlertViewDelegate method
}];
I wrote a full implementation of UIAlertView that mimics the complete UIAlertView API, but adds the contentView property we've all wanted for so long: SDCAlertView.
(source: github.io)
For those who love simple and effective methods with out having to write lines of code. Here is a cool solution without using any other private frame works for adding subviews to ios 7 alert views,i.e.
[alertView setValue:imageView forKey:#"accessoryView"];
Sample code for better understanding,
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(180, 10, 85, 50)];
UIImage *wonImage = [UIImage imageNamed:#"image.png"];
[imageView setImage:wonImage];
//check if os version is 7 or above
if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1) {
[alertView setValue:imageView forKey:#"accessoryView"];
}else{
[alertView addSubview:imageView];
}
Hope it helps some one,thanks :)
For IOS7
UIAlertView *alertView1 = [[UIAlertView alloc] initWithTitle:#"Enter Form Name"
message:#""
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Ok", nil];
alertView1.alertViewStyle = UIAlertViewStyleSecureTextInput;
UITextField *myTextField = [alertView1 textFieldAtIndex:0];
[alertView1 setTag:555];
myTextField.keyboardType=UIKeyboardTypeAlphabet;
[alertView1 show];
There wont be UIAlertView with custom views in iOS7, nor contentView which Apple changed its mind about, so addSubview is impossible now in UIAlertView.
A good alternative will be SVProgressHUD, according to many threads in Apple's forum.
Edit:
There is officially no addSubview nor subclassing for UIAlertView in iOS7.
The UIAlertView class is intended to be used as-is and does not
support subclassing. The view hierarchy for this class is private and
must not be modified.
Other good alternatives:
ios-custom-alertview by wimagguc
MZFormSheetController.
You can find simple solution without extra classes here
It is based on setting accessoryView for ordinary UIAlertView.
PKAlertController (https://github.com/goodpatch/PKAlertController) is great library. I tested a lot of similar libraries and just this satisfied all my requirements.
Why it is cool:
Supports custom view
Supports iOS7
It is view controller
It behaves and looks like native alert view, including motion effects
Customizable
Similar interface like in UIAlertController
I'm trying to display a UIDocumentInteractionController on my app. Everything is working perfectly on iPhone, but nothing is happening on iPad. Here is my code:
interactionController = [UIDocumentInteractionController interactionControllerWithURL:imageFile];
interactionController.UTI = #"com.instagram.photo";
interactionController.annotation = [NSDictionary dictionaryWithObject:[self commentForInstagram] forKey:#"InstagramCaption"];
[interactionController presentOpenInMenuFromRect:self.view.frame inView:self.view animated:YES];
interactionController is a strong reference to an instance, and imageFile exists. On iPhone, it brings up the 'Open With..' dialog and Instagram is present. On iPad, absolutely nothing happens when the above code runs. Yes, I do have Instagram installed and working on my iPad.
What could be the reason that nothing is happening when the code is executed? self.view and self.view.frame are valid objects (tested on debug).
Thanks, Can.
On iPad UIDocumentInteractionController appearing like Pop Up
Try something like this:
-(void)shareClick:(UIButton*)sender {
/*some code*/
CGRect rectForAppearing = [sender.superview convertRect:sender.frame toView:self.view];
[interactionController presentOptionsMenuFromRect:rect inView:self.view animated:YES];
}
For iPad you have to meet these 2 things:
Define area for DocumentActionMenu
CGRect rect = CGRectMake(0.0, 0.0, 0.0, 0.0);
[interactionController presentOpenInMenuFromRect:rect inView:self.view animated:YES];
Use iPad, not simulator
Use presentOptionsMenuFromRect:inView:animated:.
For example, if you want the menu to be presented from the bottom, try
[interactionController presentOptionsMenuFromRect:self.view.bounds inView:self.view animated:YES];
I had the same problem earlier today.
First of all, do not pass the frame of your view to presentOptionsMenuFromRect:inView:animated. The given rect is supposed to be in the coordinates of the view. The frame of the view is in the coordinates of the view's superview.
On iPhone, passing the bounds of the view worked, but on iPad, Xcode (7.2.1) would complain about unsatisfiable constraints and not display the document interaction controller's view (DIC).
Instead of the bounds, I tried to pass CGRectZero as the first parameter which anchors the DIC in the upper left corner of the view. This works but it looks bad.
In order to position the DIC at the center of the bottom edge of the view, you can specify a rect of size CGSizeZero positioned at the center of the bottom edge of the view (use the view's bounds to compute the position). This works and looks ok.
presentOptionsMenuFromRect:inView: - This is talking about where the popover arrow points.
The view is the view that the arrow points at, and the rect is the rect inside the view that the arrow points at.
Once you understand that this is easy.
- (void)shareClick:(UIButton*)sender
{
/*some code*/
[interactionController presentOptionsMenuFromRect:sender.bounds inView:sender animated:YES];
}
So now the arrow will point to an edge of the bounds of the button.
Most of the time this is what you want but you could for example inset this rect to have the arrow point inside the button.
I had the same issue. I checked the frame that is being passed and saw that x and y are set to 0. Next I tried to change those values (by keeping width and height as passed) and the popup showed up. Here the code:
-(void)openDocument:(UIView*)senderView {
CGRect rectForAppearing = [senderView convertRect:senderView.frame toView:senderView];
if (isIPAD)
rectForAppearing = CGRectMake(100, 100, rectForAppearing.size.width, rectForAppearing.size.height);
[interactionController presentOptionsMenuFromRect:rect inView:self.view animated:YES];
}
The popup appears on an Ipad in the top right corner. You can of course twist those 100,100 parameters to your desire. On iPhones i leave the popover as is (at the bottom)