In iOS15, there is a new API to SFSafariViewController.Configuration that suggest one can add a custom button on the SafariViewController UI. This blogpost explains this in greater detail, under "Running custom extension".
I've been trying to implement this in a sample app without success. See the code snippet below:
- (void)openSfariViewController {
SFSafariViewControllerConfiguration *config = [[SFSafariViewControllerConfiguration alloc] init];
// Also tried the following extensionIdentifiers without success
// com.atomicbird.DemoNotes
// com.apple.share-services
SFSafariViewControllerActivityButton *button = [[SFSafariViewControllerActivityButton alloc] initWithTemplateImage:[UIImage systemImageNamed:#"heart.fill"] extensionIdentifier:#"com.atomicbird.DemoNotes.DemoNotes"];
config.activityButton = button;
SFSafariViewController *safariVC = [[SFSafariViewController alloc]initWithURL:[NSURL URLWithString:#"http://developer.apple.com"] configuration:config];
// safariVC.delegate = self;
[self presentViewController:safariVC animated:NO completion:nil];}
However I try to configure the new activityButton, it doesn't seem to have an effect on how SFSafariViewController appears when it is presented. It looks exactly like it does if I don't configure the activityButton. Here are some things I think I might got wrong:
I don't know what is the extensionIdentifier, perhaps I used a wrong value?
Perhaps I got the whole thing wrong, and an ActivityButton is not what I think it is?
Maybe the Share extension is not the configured properly?
I haven't been able to find any information on the web on how an activityButton is even supposed to look like. Thanks for reading this far, let me know if you have any pointers for me.
Related
i want to achieve above screenshot output in one of my app below is the sample code i am looking at in developers website.
https://developers.google.com/places/ios-api/placepicker
GMSPlacePickerConfig *config = [[GMSPlacePickerConfig alloc] initWithViewport:nil];
GMSPlacePickerViewController *placePicker = [[GMSPlacePickerViewController alloc] initWithConfig:config];
placePicker.delegate = self;
[self presentViewController:placePicker animated:YES completion:nil];
GMSPlacePickerViewController is not visible to class it seems.
below attached is the error i am getting while putting this code to my class.
Let me know if anyone of you have worked on this.
Thanks in advance.
This has got to be the dumbest question of the day, but I'm just not getting it.
I create a Quicklook, which shows just fine. When I hit the Done button, it just reappears. How do I intercept the Done button? Or more generally, control what is displayed in what I assume is a navbar. Here is the relevant code:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
/*
* get the path to the pdf resource.
*/
NSString *path = [[NSBundle mainBundle] pathForResource:#"article" ofType:#"pdf"];
NSURL *docURL = [NSURL fileURLWithPath:path];
/*
* create the Quicklook controller.
*/
QLPreviewController *qlController = [[QLPreviewController alloc] init];
PreviewItem *item = [[PreviewItem alloc] initPreviewURL:docURL WithTitle:#"Article"];
self.pdfDatasource = [[PDFDataSource alloc] initWithPreviewItem:item];
qlController.dataSource = self.pdfDatasource;
/*
* present the document.
*/
[self presentViewController:qlController animated:YES completion:nil];
}
I assume I am missing something obvious.
Thank you,
Ken
Did you tried Taking your ViewDidAppear code To ViewDidLoad ? As when you click on done button all the views of the Controller are being Loaded again except ViewDidLoad. So the quicklook view Appears again. Just Try
The trick was to roll everything back into the original viewcontroller. That way when I hit the done button it goes back to the original viewcontroller, which is exactly what I wanted. so instead of having a separate class, I just incorporated the calls right into my main viewcontroller. I suspect that there is still a way to do with a cunning use of delegates, but in case anyone else is having the same issue, this was a solution that worked for me.
Thank you for your attention and help.
Ken
I'm using the UIDocumentInteractionController in iOS 7.1 and it's performing really badly.
I'm using it in a UICollectionViewController to view documents in a collection view.
On pressing an item in the collection view, it takes about around 6 (yes, that's six) seconds to appear. From a user experience perspective, they've pressed the screen a few more times before it appears because it takes so long.
I'm using the same code since iOS 6, but it seems particularly bad now. If anyone has any thoughts as to how I can speed things up, that would be greatly appreciated.
Essentially, I have the following in my header file:
interface MyViewController : UICollectionViewController <UIDocumentInteractionControllerDelegate>
{
UIDocumentInteractionController *docController;
}
#end
and in the implementation, I'm just doing the following:
In viewDidLoad (recently moved to here to see if it improves things):
docController = [[UIDocumentInteractionController alloc] init];
docController.delegate = self;
And then in the collectionView:didSelectItemAtIndexPath: I'm doing this:
NSURL *fileURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:document.Link ofType:#"" ]];
[docController setURL:fileURL];
PresentationViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"DocumentCell" forIndexPath:indexPath];
CGRect rect1 = cell.frame;
bool didShow = [docController presentOptionsMenuFromRect:rect1 inView:collectionView animated:YES];
where document is just a class with a string for the URL.
Let me know if you need any further detail.
Thanks in advance for any assistance anyone can provide.
-- Update:
After some NSLogs, I noticed that it's definitely the following line that's slow:
bool didShow = [docController presentOptionsMenuFromRect:rect1 inView:collectionView animated:YES];
TL;DR:
The method you are using is a synchronous request that uses your document data for find which apps are capable of reading your file. You need to swap with the asynchronous version that restricts the enumeration to only apps that can parse your file type.
Remove this method:
- (BOOL)presentOptionsMenuFromRect:(CGRect)rect
inView:(UIView *)view
animated:(BOOL)animated
And replace with this method:
- (BOOL)presentOpenInMenuFromRect:(CGRect)rect
inView:(UIView *)view
animated:(BOOL)animated
Excerpt from the Apple Docs:
This method is similar to the presentOptionsMenuFromRect:inView:animated: method, but presents a menu restricted to a list of apps capable of opening the current document. This determination is made based on the document type (as indicated by the UTI property) and on the document types supported by the installed apps. To support one or more document types, an app must register those types in its Info.plist file using the CFBundleDocumentTypes key.
If there are no registered apps that support opening the document, the document interaction controller does not display a menu.
This method displays the options menu asynchronously. The document interaction controller dismisses the menu automatically when the user selects an appropriate option. You can also dismiss it programmatically using the dismissMenuAnimated: method.
I was encountering a similar problem with:
UIDocumentInteractionController.presentPreviewAnimated
It would take an incredibly long time to complete. I found adding a brief delay between saving the file to be previewed and presenting the preview fixed the problem:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(100 * NSEC_PER_MSEC)), dispatch_get_main_queue(), {
self.controller.presentPreviewAnimated(false)
})
Swift 4.2
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.controller.presentPreviewAnimated(false)
}
I have an EAGLView-based class that runs the following code when a menu selection is made in OpenGL:
-(void) startPicker
{
self.gameState = kStatePicker;
GKPeerPickerController *picker = [[GKPeerPickerController alloc] init];
picker.delegate = self;
picker.connectionTypesMask = GKPeerPickerConnectionTypeNearby;
[picker show];
}
For some unknown reason, the picker is a blank rounded rect when it appears, with no visible interface or directions.
If I launch it at the end of my init function, it works. Afterward, it launches fine from the menu.
I have tried placing the code in startPicker inside a main queue dispatch, but that doesn't seem to help. I've tried running the picker with and without ARC, but that makes no difference. This code is more or less directly taken from the GKTank example that Apple provided a while ago, to introduce GameKit's bluetooth framework.
Can anyone tell me why this might be happening, and what a possible solution is?
Just following up. I discovered in the view controller that contains this view that there was a call to [UIView setAnimations:NO]; This prohibits GKPeerPickerController from appearing properly.
I have an iPad application that uses WebViews to display a list of URL's. I would like to be able to print all of the WebViews in one go, without prompting the user multiple times with the PrintInteractionController. The problem is that the PrintInteractionController does not appear to have the ability to do this. You cannot assign multiple viewFormatters, and the WebViews are not recognized as printItems. There is also no method that I can find to just print the items and not show the PrintInteractionController.
Does anyone know of a way to do this?
Cheers.
This is an old question, but I spent several days searching for an answer and following some misleading comments in the Apple docs and sample code. So, I'm sticking this here to save the next guy the days I wasted.
The specific problem is: How does one write a UIPrintPageRenderer that can print multiple UIWebViews in a single print job given to a UIPrintInteractionController?
You can get pretty far along to a solution that doesn't involve converting everything to PDFs first using this Apple sample code: PrintWebView. Also this documentation will help some: UIPrintPageRenderer. The problem is that the sample App and the documentation for UPPrintPageRenderer suggest that if you add multiple UIPrintFormatter via this property:
#property(nonatomic,copy) NSArray <UIPrintFormatter *> *printFormatters
The sample code Apple provided for the method to override -(NSInteger)numberOfPages in PrintWebView will just work and figure out the correct page counts.... Nope!
What you have to do is add the printFormatters in a non-obvious way via the method shown below, and then correct their respective startPage properties and use these collectively to work out the correct pageCount for UIPrintPageRenderer to return. Instead, use this:
-(void)addPrintFormatter:(UIPrintFormatter *)formatter startingAtPageAtIndex:(NSInteger)pageIndex
Why does this works and the other doesn't? No idea, probably a bug.... I should file a radar :) But Thanks to #Mustafa and this other Stackoverflow answer for the hint:
Here are the steps to follow to print multiple UIWebViews in one print job:
Follow the Apple outline in the PrintWebView example referenced above and write your UIPrintPageRenderer to set the properties you want on the webView.viewPrintFormatters you give to it.
Add the printFormatters via: [addPrintFormatter:startingAtPageAtIndex:]
In your UIPrintPageRenderer -(NSInteger)numberOfPages method get the pageCount from each formatter, and use that to update the startPage and total page count values.
Here is the basic code outline to follow (FYI: this solution is working for an App built with a deployment target of iOS 9.0):
Define your UIPrintPageRenderer class like so (you don't really need the init method, but in my use case I set values in there I always want that way):
#interface MyPrintPageRenderer : UIPrintPageRenderer
#end
#implementation MyPrintPageRenderer
-(id)init
{
self = [super init];
if(self) {
self.headerHeight = 0;
self.footerHeight = 0;
}
return self;
}
//
// Set whatever header, footer and insets you want. It is
// important to set these values to something, so that the print
// formatters can figure out their own pageCounts for whatever
// they contain. Look at the Apple sample App for PrintWebView for
// for more details about these values.
//
-(NSInteger)numberOfPages
{
__block NSUInteger startPage = 0;
for(UIPrintFormatter *pf in self.printFormatters) {
pf.contentInsets = UIEdgeInsetsZero;
pf.maximumContentWidth = self.printableRect.size.width;
pf.maximumContentHeight = self.printableRect.size.height;
dispatch_async(dispatch_get_main_queue(), ^{
pf.startPage = startPage;
startPage = pf.startPage + pf.pageCount;
});
}
return [super numberOfPages];
}
#end
In the Controller that handles the print action for the UIWebViews you want to print, do something like this:
-(void)printWebViews
{
MyPrintPageRenderer *pr = [MyPrintPageRenderer new];
//
// Add the printFormatters at sequential startingPageIndexes,
// your renderer will set them to the correct values later based
// on the various page metrics and their content.
//
[pr addPrintFormatter:_webView1.viewPrintFormatter startingAtPageAtIndex:0];
[pr addPrintFormatter:_webView2.viewPrintFormatter startingAtPageAtIndex:1];
[pr addPrintFormatter:_webView3.viewPrintFormatter startingAtPageAtIndex:2];
// Set whatever values needed for these as per Apple docs
UIPrintInfo *pi = [UIPrintInfo printInfo];
pi.outputType = UIPrintInfoOutputGeneral;
UIPrintInteractionController *pic = [UIPrintInteractionController sharedPrintController];
pic.printInfo = pi;
pic.printPageRenderer = pr;
[pic presentAnimated:NO completionHandler:nil];
}
And voila! Now... it just works ;)
Use printPageRenderer property of your UIPrintIterationController object. You can set multiple UIPrintFormatter subclasses in a UIPrintPageRenderer subclass object.