Printing a local PDF using Swift - swift

I have a printing routine in Objective-C that prints a local PDF file.
I would like to get the same routine working in Swift. Can anyone help?
- (void)printFile:(NSURL *)url {
if ([UIPrintInteractionController .canPrintURL(url]) {
UIPrintInteractionController *
controller = [UIPrintInteractionController
sharedPrintController()];
controller.printingItem = url;
UIPrintInfo *printInfo = [UIPrintInfo printInfo];
PrintInfo.outputType = UIPrintInfoOutputGeneral;
PrintInfo.jobName = [url lastPathComponent];
controller.printInfo = printInfo;
controller.showPageRange = YES;
[controller presentAnimated:YES completionHandler:Null];
}
}

This should point you in the right direction, follow the link to get a good idea of what this does.
#IBAction func print(sender: UIBarButtonItem) {
if UIPrintInteractionController.canPrintURL(imageURL) {
let printInfo = UIPrintInfo(dictionary: nil)
printInfo.jobName = imageURL.lastPathComponent
printInfo.outputType = .Photo
let printController = UIPrintInteractionController.sharedPrintController()!
printController.printInfo = printInfo
printController.showsNumberOfCopies = false
printController.printingItem = imageURL
printController.presentAnimated(true, completionHandler: nil)
}
}
taken from http://nshipster.com/uiprintinteractioncontroller/

Update for swift 4.2
#IBAction func printAction(_ sender: UIButton) {
if let guide_url = Bundle.main.url(forResource: "RandomPDF", withExtension: "pdf"){
if UIPrintInteractionController.canPrint(guide_url) {
let printInfo = UIPrintInfo(dictionary: nil)
printInfo.jobName = guide_url.lastPathComponent
printInfo.outputType = .photo
let printController = UIPrintInteractionController.shared
printController.printInfo = printInfo
printController.showsNumberOfCopies = false
printController.printingItem = guide_url
printController.present(animated: true, completionHandler: nil)
}
}
}

Related

Swift: Use AirPrint and UIPrintInteractionController to print PostStamp onto an envelope

I would like to print an image (post stamp) onto an envelope with AirPrint. It works with an "DIN A4" paper but not with "Envelope DL".
#IBAction func printMenu(_ sender: Any) {
let printController = UIPrintInteractionController.shared
let printInfo = UIPrintInfo(dictionary: nil)
printInfo.outputType = .general
printInfo.orientation = .portrait
printInfo.jobName = "myPrintJob"
printController.printInfo = printInfo
let paperSize :CGSize = CGSize(width: 831.49606299, height: 415.7480315)
printController.printingItem = createIMG(paperSize: paperSize)
printController.present(animated: true, completionHandler: nil)
}
Does anyone know a solution for that challenge? Can I choose the paperSize?

print view controller [duplicate]

I am developing an app which requires visitor passes to be generated and printed directly from an iPad over AirPrint.
I have looked everywhere to find out how to print a view but I can only find how to print text, webKit and mapKit.
Is there a way of printing an entire view? If not, what would be a good solution to print a visitor pass which will be plain text, boxes and a photograph. Thanks.
I have found the answer to my question by modifying the code found here: AirPrint contents of a UIView
//create an extension to covert the view to an image
extension UIView {
func toImage() -> UIImage {
UIGraphicsBeginImageContextWithOptions(bounds.size, false, UIScreen.mainScreen().scale)
drawViewHierarchyInRect(self.bounds, afterScreenUpdates: true)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
//In your view controller
#IBAction func printButton(sender: AnyObject) {
let printInfo = UIPrintInfo(dictionary:nil)
printInfo.outputType = UIPrintInfoOutputType.General
printInfo.jobName = "My Print Job"
// Set up print controller
let printController = UIPrintInteractionController.sharedPrintController()
printController.printInfo = printInfo
// Assign a UIImage version of my UIView as a printing iten
printController.printingItem = self.view.toImage()
// If you want to specify a printer
guard let printerURL = URL(string: "Your printer URL here, e.g. ipps://HPDC4A3E0DE24A.local.:443/ipp/print") else { return }
guard let currentPrinter = UIPrinter(url: printerURL) else { return }
printController.print(to: currentPrinter, completionHandler: nil)
// Do it
printController.presentFromRect(self.view.frame, inView: self.view, animated: true, completionHandler: nil)
}
I think you have to look print photo sample code with Swift:
https://developer.apple.com/library/ios/samplecode/PrintPhoto/Introduction/Intro.html
What exactly is your view, imageView or UIView? If you are interested in imageView or UIImage, Print Photo sample from Apple is for you. If your subject is UIView you can create pdf context from view.layers and send to AirPrint func like WebKit, text or you can print to create pdf data.
The best solution is Create Pdf file is in here for swift
Generate PDF with Swift
Print pdf file is for swift implementation:
var pdfLoc = NSURL(fileURLWithPath:yourPdfFilePath)
let printController = UIPrintInteractionController.sharedPrintController()!
let printInfo = UIPrintInfo(dictionary:nil)!
printInfo.outputType = UIPrintInfoOutputType.General
printInfo.jobName = "print Job"
printController.printInfo = printInfo
printController.printingItem = pdfLoc
printController.presentFromBarButtonItem(printButton, animated: true, completionHandler: nil)
Swift 5:
let info = UIPrintInfo(dictionary:nil)
info.outputType = UIPrintInfo.OutputType.general
info.jobName = "Printing"
let vc = UIPrintInteractionController.shared
vc.printInfo = info
vc.printingItem = UIImage.image(fromView: self.view) // your view here
vc.present(from: self.view.frame, in: self.view, animated: true, completionHandler: nil)
extension UIImage {
/// Get image from given view
///
/// - Parameter view: the view
/// - Returns: UIImage
public class func image(fromView view: UIView) -> UIImage {
UIGraphicsBeginImageContextWithOptions(view.frame.size, false, 0)
view.drawHierarchy(in: view.bounds, afterScreenUpdates: false)
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image
}
}
Hier in Swift 3.x
func prt() {
let printInfo = UIPrintInfo(dictionary:nil)
printInfo.outputType = UIPrintInfoOutputType.general
printInfo.jobName = "My Print Job"
// Set up print controller
let printController = UIPrintInteractionController.shared
printController.printInfo = printInfo
// Assign a UIImage version of my UIView as a printing iten
printController.printingItem = self.view.toImage()
// Do it
printController.present(from: self.view.frame, in: self.view, animated: true, completionHandler: nil)
}
}
extension UIView {
func toImage() -> UIImage {
UIGraphicsBeginImageContextWithOptions(bounds.size, false, UIScreen.main.scale)
drawHierarchy(in: self.bounds, afterScreenUpdates: true)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image!
}
}

Add Instagram to UIActivityViewController

I'm trying to share an image using standard UIActivityViewController, it's fine to share on Facebook, Twitter and Save Image using this code:
let firstActivityItem = "foo text"
let secondActivityItem : UIImage = image!
let activityViewController : UIActivityViewController = UIActivityViewController(
activityItems: [firstActivityItem, secondActivityItem], applicationActivities: nil)
activityViewController.excludedActivityTypes = [
UIActivityTypePostToWeibo,
UIActivityTypePrint,
UIActivityTypeAssignToContact,
UIActivityTypeAddToReadingList,
UIActivityTypePostToVimeo,
UIActivityTypePostToTencentWeibo
]
self.presentViewController(activityViewController, animated: true, completion: nil)
I need one more thing, Instagram:
If UIApplication.sharedApplication().canOpenURL(instagramURL!) {
// Success
var img = image!
var savePath: String = NSHomeDirectory().stringByAppendingPathComponent("Documents/Test.igo")
UIImageJPEGRepresentation(img, 1).writeToFile(savePath, atomically: true)
var imgURL = NSURL(string: NSString(format: "file://%#", savePath) as! String)
docController = UIDocumentInteractionController(URL: imgURL!) // 1
docController.UTI = "com.instagram.exclusivegram" // 2
docController.delegate = self
docController.annotation = ["InstagramCaption":"testsss"] // 3
docController.presentOpenInMenuFromRect(self.view.frame, inView: self.view, animated: true) // 4
} else {
// Error
}
Both these codes work fine separately, how can I add Instagram to the UIActivityViewController? Is it possible at all?
I think it would be very easier to add other social shares to the code you wrote for Instagram. The ".igo" extension is exclusive for Instagram so other apps will not support it. Just change this extension from ".igo" to ".ig" and other apps will read it:
var savePath: String = NSHomeDirectory().stringByAppendingPathComponent("Documents/Test.ig")
But Instagram also have an exclusive UTI to avoiding other apps to appear in the same Document Interaction View. So you will also need to change it from "exclusivegram" to "photo":
docController.UTI = "com.instagram.photo"
I have an app with a similar functionality and this is my original code:
#IBAction func shareOnIntagram(sender: UIButton) {
let finalImage: UIImage = UIImage.imageWithView(photoView)
let instagramURL = NSURL(string: "instagram://app")
if (UIApplication.sharedApplication().canOpenURL(instagramURL!)) {
let imageData = UIImageJPEGRepresentation(finalImage, 1)
let captionString = "caption"
let writePath = (NSTemporaryDirectory() as NSString).stringByAppendingPathComponent("instagram.ig")
if imageData?.writeToFile(writePath, atomically: true) == false {
return
} else {
let fileURL = NSURL(fileURLWithPath: writePath)
self.documentController = UIDocumentInteractionController(URL: fileURL)
self.documentController.delegate = self
self.documentController.UTI = "com.instagram.photo"
self.documentController.annotation = NSDictionary(object: captionString, forKey: "InstagramCaption")
self.documentController.presentOpenInMenuFromRect(self.view.frame, inView: self.view, animated: true)
}
} else {
print(" Instagram is not installed ")
}
}
To make this code work, don't forget to add UIDocumentInteractionControllerDelegate in the UIViewController class.
It seems it's not possible, because of .igo extension which is needed by Instagram.

Share image/text through WhatsApp in an iOS app

Is it possible to share images, text or whatever you want through Whatsapp in a iOS app? I'm searching on google but I only found results talking about Android implementations.
Is now possible in this way:
Send Text - Obj-C
NSString * msg = #"YOUR MSG";
NSString * urlWhats = [NSString stringWithFormat:#"whatsapp://send?text=%#",msg];
NSURL * whatsappURL = [NSURL URLWithString:[urlWhats stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
if ([[UIApplication sharedApplication] canOpenURL: whatsappURL]) {
[[UIApplication sharedApplication] openURL: whatsappURL];
} else {
// Cannot open whatsapp
}
Send Text - Swift
let msg = "YOUR MSG"
let urlWhats = "whatsapp://send?text=\(msg)"
if let urlString = urlWhats.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet()) {
if let whatsappURL = NSURL(string: urlString) {
if UIApplication.sharedApplication().canOpenURL(whatsappURL) {
UIApplication.sharedApplication().openURL(whatsappURL)
} else {
// Cannot open whatsapp
}
}
}
Send Image - Obj-C
-- in .h file
<UIDocumentInteractionControllerDelegate>
#property (retain) UIDocumentInteractionController * documentInteractionController;
-- in .m file
if ([[UIApplication sharedApplication] canOpenURL: [NSURL URLWithString:#"whatsapp://app"]]){
UIImage * iconImage = [UIImage imageNamed:#"YOUR IMAGE"];
NSString * savePath = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents/whatsAppTmp.wai"];
[UIImageJPEGRepresentation(iconImage, 1.0) writeToFile:savePath atomically:YES];
_documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:savePath]];
_documentInteractionController.UTI = #"net.whatsapp.image";
_documentInteractionController.delegate = self;
[_documentInteractionController presentOpenInMenuFromRect:CGRectMake(0, 0, 0, 0) inView:self.view animated: YES];
} else {
UIAlertView * alert = [[UIAlertView alloc] initWithTitle:#"WhatsApp not installed." message:#"Your device has no WhatsApp installed." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
Send Image - Swift
let urlWhats = "whatsapp://app"
if let urlString = urlWhats.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet()) {
if let whatsappURL = NSURL(string: urlString) {
if UIApplication.sharedApplication().canOpenURL(whatsappURL) {
if let image = UIImage(named: "image") {
if let imageData = UIImageJPEGRepresentation(image, 1.0) {
let tempFile = NSURL(fileURLWithPath: NSHomeDirectory()).URLByAppendingPathComponent("Documents/whatsAppTmp.wai")
do {
try imageData.writeToURL(tempFile, options: .DataWritingAtomic)
self.documentInteractionController = UIDocumentInteractionController(URL: tempFile)
self.documentInteractionController.UTI = "net.whatsapp.image"
self.documentInteractionController.presentOpenInMenuFromRect(CGRectZero, inView: self.view, animated: true)
} catch {
print(error)
}
}
}
} else {
// Cannot open whatsapp
}
}
}
Because a new security feature of iOS 9, you need add this lines on
.plist file:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>whatsapp</string>
</array>
More information about url sheme: https://developer.apple.com/videos/play/wwdc2015-703/
I did not find a single solution for both.
More information on http://www.whatsapp.com/faq/en/iphone/23559013
I made a small project to help some.
https://github.com/salesawagner/SharingWhatsApp
It is now possible. Have not tried yet though.
The latest release notes for whatsapp indicate that you can through the share extension:
WhatsApp accepts the following types of content:
text (UTI: public.plain-text)
photos (UTI: public.image)
videos (UTI: public.movie)
audio notes and music files (UTI: public.audio)
PDF documents (UTI: com.adobe.pdf)
contact cards (UTI: public.vcard)
web URLs (UTI: public.url)
No this is not possible, whatsapp does not have any public API you can use.
Please note that this answer is correct for 2011 when there was no API for WhatsApp.
Now there is an api available for interacting with WhatsApp: http://www.whatsapp.com/faq/en/iphone/23559013
The Objective-C call to open one of these URLs is as follows:
NSURL *whatsappURL = [NSURL URLWithString:#"whatsapp://send?text=Hello%2C%20World!"];
if ([[UIApplication sharedApplication] canOpenURL: whatsappURL]) {
[[UIApplication sharedApplication] openURL: whatsappURL];
}
This is the correct code for share link to whats app users.
NSString * url = [NSString stringWithFormat:#"http://video...bla..bla.."];
url = (NSString*)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(NULL,(CFStringRef) url, NULL,CFSTR("!*'();:#&=+$,/?%#[]"),kCFStringEncodingUTF8));
NSString * urlWhats = [NSString stringWithFormat:#"whatsapp://send?text=%#",url];
NSURL * whatsappURL = [NSURL URLWithString:urlWhats];
if ([[UIApplication sharedApplication] canOpenURL: whatsappURL]) {
[[UIApplication sharedApplication] openURL: whatsappURL];
} else {
// can not share with whats app
}
Simple code and Sample code ;-)
Note:- You can only share text or image, both sharing together in whatsApp is not working from whatsApp side
/*
//Share text
NSString *textToShare = #"Enter your text to be shared";
NSArray *objectsToShare = #[textToShare];
UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:objectsToShare applicationActivities:nil];
[self presentViewController:activityVC animated:YES completion:nil];
*/
//Share Image
UIImage * image = [UIImage imageNamed:#"images"];
NSArray *objectsToShare = #[image];
UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:objectsToShare applicationActivities:nil];
[self presentViewController:activityVC animated:YES completion:nil];
For Swift 4 - Works fine
delclare
var documentInteractionController:UIDocumentInteractionController!
func sharePicture() {
let urlWhats = "whatsapp://app"
if let urlString = urlWhats.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed) {
if let whatsappURL = NSURL(string: urlString) {
if UIApplication.shared.canOpenURL(whatsappURL as URL) {
let imgURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let fileName = "yourImageName.jpg"
let fileURL = imgURL.appendingPathComponent(fileName)
if let image = UIImage(contentsOfFile: fileURL.path) {
if let imageData = image.jpegData(compressionQuality: 0.75) {
let tempFile = NSURL(fileURLWithPath: NSHomeDirectory()).appendingPathComponent("Documents/yourImageName.jpg")
do {
try imageData.write(to: tempFile!, options: .atomicWrite)
self.documentInteractionController = UIDocumentInteractionController(url: tempFile!)
self.documentInteractionController.uti = "net.whatsapp.image"
self.documentInteractionController.presentOpenInMenu(from: CGRect.zero, in: self.view, animated: true)
} catch {
print(error)
}
}
}
} else {
// Cannot open whatsapp
}
}
}
}
Do not forget to edit the .plist with the following lines
<key>LSApplicationQueriesSchemes</key>
<array>
<string>whatsapp</string>
</array>
Enjoy!!!
Swift 3 version of Wagner Sales' answer:
let urlWhats = "whatsapp://app"
if let urlString = urlWhats.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) {
if let whatsappURL = URL(string: urlString) {
if UIApplication.shared.canOpenURL(whatsappURL) {
if let image = UIImage(named: "image") {
if let imageData = UIImageJPEGRepresentation(image, 1.0) {
let tempFile = NSURL(fileURLWithPath: NSHomeDirectory()).appendingPathComponent("Documents/whatsAppTmp.wai")
do {
try imageData.write(to: tempFile!, options: .atomic)
self.documentIC = UIDocumentInteractionController(url: tempFile!)
self.documentIC.uti = "net.whatsapp.image"
self.documentIC.presentOpenInMenu(from: CGRect.zero, in: self.view, animated: true)
}
catch {
print(error)
}
}
}
} else {
// Cannot open whatsapp
}
}
}
I added a Whatsapp Sharer to ShareKit.
Check out here:
https://github.com/heringb/ShareKit
Swift 4
let urlWhats = "whatsapp://app"
if let urlString = urlWhats.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlHostAllowed) {
if let whatsappURL = URL(string: urlString) {
if UIApplication.shared.canOpenURL(whatsappURL) {
if let imageData = UIImageJPEGRepresentation(image, 1.0) {
let tempFile = NSURL(fileURLWithPath: NSHomeDirectory()).appendingPathComponent("Documents/whatsAppTmp.wai")!
do {
try imageData.write(to: tempFile, options: .atomic)
self.documentController = UIDocumentInteractionController(url: tempFile)
self.documentController.uti = "net.whatsapp.image"
self.documentController.presentOpenInMenu(from: CGRect.zero, in: self.view, animated: true)
} catch {
print(error)
}
}
} else {
let ac = UIAlertController(title: "MessageAletTitleText".localized, message: "AppNotFoundToShare".localized, preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OKButtonText".localized, style: .default))
present(ac, animated: true)
print("Whatsapp isn't installed ")
// Cannot open whatsapp
}
}
}
WhatsApp provides two ways for your iPhone app to interact with WhatsApp:
Through a custom URL scheme
Through the iOS Document Interaction API
For more information Visit this link
Thanks.
Yes it's possible :
NSMutableArray *arr = [[NSMutableArray alloc]init];
NSURL *URL = [NSURL fileURLWithPath:path];
NSString *textToShare = [NSString stringWithFormat:#"%# \n",_model.title];
NSString *SchoolName= [[AppUtility sharedUtilityInstance]getAppConfigInfoByKey:#"SchoolName" SecondKeyorNil:Nil];
[arr addObject:textToShare];
[arr addObject:URL];
[arr addObject:_model.body];
[arr addObject:SchoolName];
TTOpenInAppActivity *openInAppActivity = [[TTOpenInAppActivity alloc] initWithView:_parentController.view andRect:((UIButton *)sender).frame];
UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:arr applicationActivities:#[openInAppActivity]];
// Store reference to superview (UIActionSheet) to allow dismissal
openInAppActivity.superViewController = activityViewController;
// Show UIActivityViewController
[_parentController presentViewController:activityViewController animated:YES completion:NULL];
Swift 3 version for sending text:
func shareByWhatsapp(msg:String){
let urlWhats = "whatsapp://send?text=\(msg)"
if let urlString = urlWhats.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) {
if let whatsappURL = NSURL(string: urlString) {
if UIApplication.shared.canOpenURL(whatsappURL as URL) {
UIApplication.shared.openURL(whatsappURL as URL)
} else {
let alert = UIAlertController(title: NSLocalizedString("Whatsapp not found", comment: "Error message"),
message: NSLocalizedString("Could not found a installed app 'Whatsapp' to proceed with sharing.", comment: "Error description"),
preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("Ok", comment: "Alert button"), style: UIAlertActionStyle.default, handler:{ (UIAlertAction)in
}))
self.present(alert, animated: true, completion:nil)
// Cannot open whatsapp
}
}
}
}
Also, you need to add whatsapp to LSApplicationQueriesSchemes in your Info.plist
NSString *shareText = #"http:www.google.com";
NSArray *objectsToShare = #[shareText];
UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:objectsToShare applicationActivities:nil];
if (isIphone)
{
[self presentViewController:activityVC animated:YES completion:nil];
}
else {
UIPopoverController *popup = [[UIPopoverController alloc] initWithContentViewController:activityVC];
[popup presentPopoverFromRect:CGRectMake(self.view.frame.size.width/2, self.view.frame.size.height/4, 0, 0)inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}

How to trim the video using AVFoundation

Iam able to record the video by using AVFoundation or UIImagePickerController. But i am unable to trim the video from one particular second to another particular duration/time. Can any one help me.
Thanks,
Siva Krishna.
You can have the UIImagePickerController enable trimming
UIImagePickerController *videoRecorder = [[UIImagePickerController alloc]init];
NSArray *sourceTypes = [UIImagePickerController availableMediaTypesForSourceType:videoRecorder.sourceType];
NSLog(#"Available types for source as camera = %#", sourceTypes);
if (![sourceTypes containsObject:(NSString*)kUTTypeMovie] ) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil
message:#"Device Not Supported for video Recording." delegate:self
cancelButtonTitle:#"Yes"
otherButtonTitles:#"No",nil];
[alert show];
[alert release];
return;
}
videoRecorder.allowsEditing = YES;
Unfortunately after you get back from the imagePickerController, You are forced to convert the video manually.
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
if ([self.popoverLibraryBrowser isPopoverVisible])
{
[self.popoverLibraryBrowser dismissPopoverAnimated:YES];
}
NSString *type = [info objectForKey:UIImagePickerControllerMediaType];
if ([type isEqualToString:(NSString *)kUTTypeVideo] ||
[type isEqualToString:(NSString *)kUTTypeMovie]) { // movie != video
NSURL *videoURL = [info objectForKey:UIImagePickerControllerMediaURL];
NSNumber *start = [info objectForKey:#"_UIImagePickerControllerVideoEditingStart"];
NSNumber *end = [info objectForKey:#"_UIImagePickerControllerVideoEditingEnd"];
// if start and end are nil then clipping was not used.
// You should use the entire video.
int startMilliseconds = ([start doubleValue] * 1000);
int endMilliseconds = ([end doubleValue] * 1000);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSFileManager *manager = [NSFileManager defaultManager];
NSString *outputURL = [documentsDirectory stringByAppendingPathComponent:#"output"] ;
[manager createDirectoryAtPath:outputURL withIntermediateDirectories:YES attributes:nil error:nil];
outputURL = [outputURL stringByAppendingPathComponent:#"output.mp4"];
// Remove Existing File
[manager removeItemAtPath:outputURL error:nil];
//[self loadAssetFromFile:videoURL];
[self.recorder dismissModalViewControllerAnimated:YES];
AVURLAsset *videoAsset = [AVURLAsset URLAssetWithURL:videoURL options:nil];
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:videoAsset presetName:AVAssetExportPresetHighestQuality];
exportSession.outputURL = [NSURL fileURLWithPath:outputURL];
exportSession.outputFileType = AVFileTypeQuickTimeMovie;
CMTimeRange timeRange = CMTimeRangeMake(CMTimeMake(startMilliseconds, 1000), CMTimeMake(endMilliseconds - startMilliseconds, 1000));
exportSession.timeRange = timeRange;
[exportSession exportAsynchronouslyWithCompletionHandler:^{
switch (exportSession.status) {
case AVAssetExportSessionStatusCompleted:
// Custom method to import the Exported Video
[self loadAssetFromFile:exportSession.outputURL];
break;
case AVAssetExportSessionStatusFailed:
//
NSLog(#"Failed:%#",exportSession.error);
break;
case AVAssetExportSessionStatusCancelled:
//
NSLog(#"Canceled:%#",exportSession.error);
break;
default:
break;
}
}];
//NSData *videoData = [NSData dataWithContentsOfURL:videoURL];
//NSString *videoStoragePath;//Set your video storage path to this variable
//[videoData writeToFile:videoStoragePath atomically:YES];
//You can store the path of the saved video file in sqlite/coredata here.
}
}
Swift version of above
import UIKit
import AVFoundation
import MobileCoreServices
func pickVideo(){
if UIImagePickerController.isSourceTypeAvailable(.Camera) {
let videoRecorder = UIImagePickerController()
videoRecorder.sourceType = .Camera
videoRecorder.mediaTypes = [kUTTypeMovie as String]
videoRecorder.allowsEditing = true
videoRecorder.delegate = self
presentViewController(videoRecorder, animated: true, completion: nil)
}
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
picker.dismissViewControllerAnimated(true, completion: nil)
let manager = NSFileManager.defaultManager()
guard let documentDirectory = try? manager.URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: true) else {return}
guard let mediaType = info[UIImagePickerControllerMediaType] as? String else {return}
guard let url = info[UIImagePickerControllerMediaURL] as? NSURL else {return}
if mediaType == kUTTypeMovie as String || mediaType == kUTTypeVideo as String {
let asset = AVAsset(URL: url)
let length = Float(asset.duration.value) / Float(asset.duration.timescale)
print("video length: \(length) seconds")
let start = info["_UIImagePickerControllerVideoEditingStart"] as? Float
let end = info["_UIImagePickerControllerVideoEditingEnd"] as? Float
var outputURL = documentDirectory.URLByAppendingPathComponent("output")
do {
try manager.createDirectoryAtURL(outputURL, withIntermediateDirectories: true, attributes: nil)
outputURL = outputURL.URLByAppendingPathComponent("output.mp4")
}catch let error {
print(error)
}
//Remove existing file
_ = try? manager.removeItemAtURL(outputURL)
guard let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetHighestQuality) else {return}
exportSession.outputURL = outputURL
exportSession.outputFileType = AVFileTypeMPEG4
let startTime = CMTime(seconds: Double(start ?? 0), preferredTimescale: 1000)
let endTime = CMTime(seconds: Double(end ?? length), preferredTimescale: 1000)
let timeRange = CMTimeRange(start: startTime, end: endTime)
exportSession.timeRange = timeRange
exportSession.exportAsynchronouslyWithCompletionHandler{
switch exportSession.status {
case .Completed:
print("exported at \(outputURL)")
case .Failed:
print("failed \(exportSession.error)")
case .Cancelled:
print("cancelled \(exportSession.error)")
default: break
}
}
}
}
The best solution for swift 4, i have found there. I did fixes it for my needs, but it's really clear and convenience.
The code:
import AVFoundation
import Foundation
extension FileManager {
func removeFileIfNecessary(at url: URL) throws {
guard fileExists(atPath: url.path) else {
return
}
do {
try removeItem(at: url)
}
catch let error {
throw TrimError("Couldn't remove existing destination file: \(error)")
}
}
}
struct TrimError: Error {
let description: String
let underlyingError: Error?
init(_ description: String, underlyingError: Error? = nil) {
self.description = "TrimVideo: " + description
self.underlyingError = underlyingError
}
}
extension AVMutableComposition {
convenience init(asset: AVAsset) {
self.init()
for track in asset.tracks {
addMutableTrack(withMediaType: track.mediaType, preferredTrackID: track.trackID)
}
}
func trim(timeOffStart: Double) {
let duration = CMTime(seconds: timeOffStart, preferredTimescale: 1)
let timeRange = CMTimeRange(start: kCMTimeZero, duration: duration)
for track in tracks {
track.removeTimeRange(timeRange)
}
removeTimeRange(timeRange)
}
}
extension AVAsset {
func assetByTrimming(timeOffStart: Double) throws -> AVAsset {
let duration = CMTime(seconds: timeOffStart, preferredTimescale: 1)
let timeRange = CMTimeRange(start: kCMTimeZero, duration: duration)
let composition = AVMutableComposition()
do {
for track in tracks {
let compositionTrack = composition.addMutableTrack(withMediaType: track.mediaType, preferredTrackID: track.trackID)
try compositionTrack?.insertTimeRange(timeRange, of: track, at: kCMTimeZero)
}
} catch let error {
throw TrimError("error during composition", underlyingError: error)
}
return composition
}
func export(to destination: URL) throws {
guard let exportSession = AVAssetExportSession(asset: self, presetName: AVAssetExportPresetPassthrough) else {
throw TrimError("Could not create an export session")
}
exportSession.outputURL = destination
exportSession.outputFileType = AVFileType.m4v
exportSession.shouldOptimizeForNetworkUse = true
let group = DispatchGroup()
group.enter()
try FileManager.default.removeFileIfNecessary(at: destination)
exportSession.exportAsynchronously {
group.leave()
}
group.wait()
if let error = exportSession.error {
throw TrimError("error during export", underlyingError: error)
}
}
}
func time(_ operation: () throws -> ()) rethrows {
let start = Date()
try operation()
let end = Date().timeIntervalSince(start)
print(end)
let sourceURL = URL(fileURLWithPath: CommandLine.arguments[1])
let destinationURL = URL(fileURLWithPath: CommandLine.arguments[2])
do {
try time {
let asset = AVURLAsset(url: sourceURL)
let trimmedAsset = try asset.assetByTrimming(timeOffStart: 1.0)
try trimmedAsset.export(to: destinationURL)
}
} catch let error {
print("💩 \(error)")
}
}
you should add kUTTypeMovie in the setMediaTypes array and it will work.