IPAD Presenting a UIActivityViewController from a UIAlertController style UIAlertControllerActionSheet "unable to simultaneously satisfy constraints" - ipad

I am presenting a UIAlertController as an action sheet, with a button that should open an ActivityView when selected....
Both of these methods are in the same ViewController class... and work perfectly fine on iphone 8, iPhone Pro 11 Max, etc.. but fail when testing on iPad. (All running 13.5)
Original Failures were that popoverPresentationController did not have a sourceview or barbuttonitem set (or something like that) so I added the line:
activityViewController.popoverPresentationController.sourceView = [self view];
to the share method. This got me past that fine... So, now the IPAD does not crash with a GenericException.. but it never presents the activityView either. It just throws a bunch of constraint violations that appear to be related to the ActivityView attempting to be displayed,
[LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"<NSLayoutConstraint:0x6000000d2580 LPLinkView:0x7fda08498900.leading == UILayoutGuide:0x600001a39dc0'UIViewLayoutMarginsGuide'.leading (active)>",
"<NSLayoutConstraint:0x6000000d23f0 H:[LPLinkView:0x7fda08498900]-(59)-| (active, names: '|':_UIActivityContentTitleView:0x7fda08493b90 )>",
"<NSLayoutConstraint:0x6000000cf660 H:|-(0)-[_UIActivityContentTitleView:0x7fda08493b90] (active, names: '|':_UINavigationBarContentView:0x7fda085d5550 )>",
"<NSLayoutConstraint:0x6000000cf6b0 _UIActivityContentTitleView:0x7fda08493b90.trailing == _UINavigationBarContentView:0x7fda085d5550.trailing (active)>",
"<NSLayoutConstraint:0x6000000d08c0 'UIView-Encapsulated-Layout-Width' _UINavigationBarContentView:0x7fda085d5550.width == 0 (active)>",
"<NSLayoutConstraint:0x6000000d2260 'UIView-leftMargin-guide-constraint' H:|-(16)-[UILayoutGuide:0x600001a39dc0'UIViewLayoutMarginsGuide'](LTR) (active, names: '|':_UIActivityContentTitleView:0x7fda08493b90 )>"
)
but I have no constraints set up on this..and the complaints area about references to a navigationbar which my app does not have or use. These constraints must be coming from some default settings that are only relevant on the IPAD. Does anyone know what I need to be doing here to fix this on the IPAD? Why the hell is the default constraints related to something that I never set.. I only set the sourceView.
-(UIAlertController *)displayActionSheet
{
UIAlertController *sheet = [UIAlertController
alertControllerWithTitle:nil
message:nil
preferredStyle:UIAlertControllerStyleActionSheet];
.
.
.
UIAlertAction *share = [UIAlertAction
actionWithTitle:NSLocalizedString(#"Share with Friends", #"Share with Friends")
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action){
[self share];
}];
[sheet addAction:share];
.
.
.
[sheet popoverPresentationController].sourceView = [self view];
sheet.popoverPresentationController.permittedArrowDirections = 0;
[sheet popoverPresentationController].sourceRect = CGRectMake(view.bounds.size.width/2, view.bounds.size.height/2, 0, 0);
[self presentViewController:sheet animated:YES completion:nil];
return sheet;
}
-(void)share
{
NSString *shareString = #"a string to share";
UIImage *shareImage = [UIImage imageNamed:#"animagetoshare"];
NSURL *shareUrl = [NSURL URLWithString:#"a url to share"];
NSArray *activityItems = [NSArray arrayWithObjects:shareString, shareImage, shareUrl, nil];
UIActivityViewController *activityViewController = [[[UIActivityViewController alloc]
initWithActivityItems:activityItems applicationActivities:nil] autorelease];
activityViewController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
activityViewController.excludedActivityTypes = #[UIActivityTypePrint,
UIActivityTypeCopyToPasteboard, UIActivityTypeAssignToContact,
UIActivityTypeSaveToCameraRoll, UIActivityTypeAddToReadingList];
activityViewController.popoverPresentationController.sourceView = [self view];
[self presentViewController:activityViewController animated:YES completion:nil];
}

Of course, I find the resolution 5 seconds after I post this:
UIActivityViewController, Unable to simultaneously satisfy constraints, on devices 8.x when compiled for iOS 7.x
However, this is just bizarre... why would I need to set the source rect if I set the source View... shouldn't it derive from this somehow? I am assuming the sourceRect being set is simply causing the constraints to be ignored?

Related

Is there any way to encapsulate UIAlertController within another UIViewController?

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];
}

UIImagePickerController messes up constraint based layout?

For part of my storyboard, I have some screens that are meant for editing information about an asset (name, photo, location, etc). So I wanted to use a table with static cells. And I'm trying to use constraint based layouts. In the storyboard editor, I have the following:
The configurations of my UITableView and UITableViewController are all default. Below the Photo label, is an UIImageView. It's constraints are configured as such:
Basically, it's supposed to be as wide as the cell, and forced to be a square. It's butted up against the bottom of the Photo label (which is butted to the top, and centered horizontally). No amount of Update Frames in XCode6 will make it resize as desired in the storyboard. However, when I run the app, on initial open it looks as it should:
I have an option to update the photo, using an UIImagePickerController with code as follows:
#pragma mark - Actions
- (IBAction)takePicture {
if ([UIImagePickerController isSourceTypeAvailable: UIImagePickerControllerSourceTypeCamera] == NO) {
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:nil message:#"Camera not Available" delegate:nil cancelButtonTitle: #"OK" otherButtonTitles: nil];
[alert show];
}
else {
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.allowsEditing = YES;
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
[self presentViewController: picker animated: YES completion: NULL];
}
}
#pragma mark - ImagePicker
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
[picker dismissViewControllerAnimated:YES completion: nil];
UIImage *chosenImage = info[UIImagePickerControllerEditedImage];
if (chosenImage) {
[self updatePhoto: chosenImage];
self.savePhoto = YES;
}
}
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
[picker dismissViewControllerAnimated:YES completion: nil];
};
After I run this code, regardless of whether I cancel the picker or not, two things happen:
1) The original view gets messed up
(it squashed the photo view into the 44 pixel standard height cell)
2) The XCode console spewed the following:
2014-10-20 15:29:52.715 myValve[5489:1760528] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)
(
"<NSLayoutConstraint:0x17008cf80 UIImageView:0x1701ea900.width == UIImageView:0x1701ea900.height>",
"<NSLayoutConstraint:0x178292930 UIImageView:0x1701ea900.leading == UITableViewCellContentView:0x1783868d0.leadingMargin>",
"<NSLayoutConstraint:0x178292c00 V:[UILabel:0x147e60cf0'Photo']-(0)-[UIImageView:0x1701ea900]>",
"<NSLayoutConstraint:0x1782990a0 UITableViewCellContentView:0x1783868d0.bottomMargin == UIImageView:0x1701ea900.bottom>",
"<NSLayoutConstraint:0x1782967b0 UITableViewCellContentView:0x1783868d0.trailingMargin == UIImageView:0x1701ea900.trailing>",
"<NSLayoutConstraint:0x178299050 UILabel:0x147e60cf0'Photo'.top == UITableViewCellContentView:0x1783868d0.topMargin>",
"<NSLayoutConstraint:0x1782934c0 'UIView-Encapsulated-Layout-Width' H:[UITableViewCellContentView:0x1783868d0(320)]>",
"<NSLayoutConstraint:0x178293650 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x1783868d0(43.5)]>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x17008cf80 UIImageView:0x1701ea900.width == UIImageView:0x1701ea900.height>
What's going on here? Why can it initially satisfy my constraints, give me no warnings, and show correctly, but after opening and canceling the UIImagePickerControler, everything's messed up? Is there some additional support methods I need to implement in my UITableViewController subclass?
UPDATE
I decided to probe the constrains of the contentView before and after the UIImagePickerController has opened. The constraints array has the same elements, but one of them changes its value, before:
"<NSLayoutConstraint:0x170285e60 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x178381a00(340.5)]>",
and after:
"<NSLayoutConstraint:0x170285e60 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x178381a00(43.5)]>",
The height constraint changes from 43.5 to 340.5. Why?

UIDatePickerView issue since update to ios 8

Ive just updated to iOS 8 and I have a problem with my application, everything but the date pickers on the action sheet work. When the action is selected for the action sheet to display along with the date picker added as a subview, the app crashes. I found the problem code which is :
-(void)willPresentActionSheet:(UIActionSheet*)actionSheet{
theDatePicker = [[UIDatePicker alloc]initWithFrame:CGRectMake(0, 40, 320, 216)];
[actionSheet addSubview:theDatePicker];
NSArray *subviews = [actionSheet subviews];
[[subviews objectAtIndex:0]setFrame:CGRectMake(20, 266, 280, 46)]; //this line
[[subviews objectAtIndex:1]setFrame:CGRectMake(20, 317, 280, 46)]; //also this one
}
The console log produces this :
-[__NSArrayM objectAtIndex:]: index 1 beyond bounds [0 .. 0]'
Previously I solved it (badly) by changing each index to 2 & 3 which worked but was a messy implementation.
This is the actual implementation:
-(void)willPresentActionSheet:(UIActionSheet*)actionSheet{
theDatePicker = [[UIDatePicker alloc]initWithFrame:CGRectMake(0, 40, 320, 216)];
[actionSheet addSubview:theDatePicker];
NSArray *subviews = [actionSheet subviews];
[[subviews objectAtIndex:0]setFrame:CGRectMake(20, 266, 280, 46)];
[[subviews objectAtIndex:1]setFrame:CGRectMake(20, 317, 280, 46)];
}
-(void)datePickerViewFromInput:(NSString*)title{
SheetPicker=[[UIActionSheet alloc]initWithTitle: title delegate:self cancelButtonTitle:#"Cancel" destructiveButtonTitle:nil otherButtonTitles:#"Set", nil];
[SheetPicker showInView:self.view ];
[SheetPicker setFrame:CGRectMake(0,117, 325, 383)];
theDatePicker.datePickerMode = UIDatePickerModeDate;
NSDateFormatter *Formatter = [[NSDateFormatter alloc]init];
[Formatter setDateFormat:#"MM/dd/yyyy"];
}
If i remove the problem code, the action sheet will show, but there is no longer a date picker. (I have no idea why)
Any ideas on how to fix this?
Thanks in advance
Probably apple change the view hierarchy, there is a note in the docs that does not really cover your case but still gives us an hint, what is going on:
UIActionSheet is not designed to be subclassed, nor should you add views to its hierarchy. If you need to present a sheet with more customization than provided by the UIActionSheet API, you can create your own and present it modally with presentViewController:animated:completion:.
Just as with UIAlerView apple doesn't want us to change it's experience- There-for they do some tinkering with the view hierarchy.
The sanest solution would be to do as suggested by apple: create your one view controller and present it modally.
There is a slide change to UIActionSheet in iOS 8. try this code.
UIAlertController *alert = [UIAlertController alertControllerWithTitle:#"" message:#"" preferredStyle:UIAlertControllerStyleActionSheet];
[alert.view addSubview:theDatePicker];
[self presentViewController:alert animated:YES completion:nil];

Not able to change text size of actionsheet button text

Hi I am facing very different kind of problem. I am using actionsheet in my iPhone application. So when I use predefine values of otherButtonTitles and if I try to change the text size then it works fine. But when I add buttons dynamically then it's not working properly.
My code looks like this:
- (void)showActions
{
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:nil
delegate:self
cancelButtonTitle:cancelTitle
destructiveButtonTitle:nil
otherButtonTitles:notifications, about, faq, settings, logout, nil];
[actionSheet showInView:self.view];
}
- (void)willPresentActionSheet:(UIActionSheet *)actionSheet {
for (UIView *_currentView in actionSheet.subviews) {
if ([_currentView isKindOfClass:[UIButton class]]) {
UIButton *button = (UIButton *)_currentView;
button.titleLabel.font = [UIFont systemFontOfSize:FONT_SIZE_16];
}
}
}
So above thing working fine. But if I try following things its not working
-(void) showCategories
{
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:nil
delegate:self
cancelButtonTitle:nil
destructiveButtonTitle:nil
otherButtonTitles:nil];
// categoryData is string array....
for (NSString *category in categoryData) {
[actionSheet addButtonWithTitle:category];
}
actionSheet.cancelButtonIndex = [actionSheet addButtonWithTitle:#"Cancel"];
[actionSheet showInView:self.view];
}
And I tried to change text size like above then its not working. Is there any reason? Need help. Thank you.
When total number of buttons exceeds height to display without
scrolling this problem occurs. (If anyone else finds the way then
please post an answer. But i think there's no answer to this !!)
On 4 inch device (640x1136 resolution), number of max buttons displayed are 11
On 3.5 inch device (640x960 resolution), number of max buttons displayed are 8.
A piece of advice:: I think if you use actionsheet this way to display more than 10 elements, Apple could reject your app. Use UIPickerView for this kind of behaviour.

missing separator line between Actionitems in ActionSheet ios [duplicate]

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.

Resources