How to get the QLPreviewView Content and write to an image - objective-c

I just write a small demo. All of its feature is use the QLPreviewView to give a quick look of a Pages file.
When the App runs, you can scroll to view the Pages file content, and when you click save to PNG button, the app will save the current content displayed into a PNG image file. You can get the implementation in the save method. I just tried two implementation in that method, neither of them worked.
I just got a blank image filled with the window background color. Some advise here? Thanks.
The code and the App screen shot can be found here http://dr.ibuick.com/updU
#import <Cocoa/Cocoa.h>
#import <Quartz/Quartz.h>
#import <QuickLook/QuickLook.h>
#import "IBAppDelegate.h"
#interface IBAppDelegate (QLPreviewItem) <QLPreviewItem>
#end
#implementation IBAppDelegate (QLPreviewItem)
- (NSURL *)previewItemURL
{
return self.resolvedFileURL;
}
- (NSString *)previewItemTitle
{
return [self.originalURL absoluteString];
}
#end
#implementation IBAppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
_resolvedFileURL = [NSURL fileURLWithPath:#"/Users/buick/Desktop/1.pages"];
_originalURL = [NSURL fileURLWithPath:#"/Users/buick/Desktop/1.pages"];
_previewView = [[QLPreviewView alloc] initWithFrame:NSMakeRect(0, 50, 480, 360)
style:QLPreviewViewStyleNormal];
[_previewView setPreviewItem:self];
[self.window.contentView addSubview:_previewView];
}
- (IBAction)save:(id)sender {
// Method 1
[_previewView lockFocus];
NSBitmapImageRep* rep = [_previewView
bitmapImageRepForCachingDisplayInRect:_previewView.bounds];
[_previewView cacheDisplayInRect:_previewView.bounds toBitmapImageRep:rep];
[_previewView unlockFocus];
[[rep representationUsingType:NSPNGFileType properties:nil]
writeToFile:#"/Users/buick/Desktop/1.png" atomically:YES];
// Method 2
[_previewView lockFocus];
NSBitmapImageRep *bits;
bits = [[NSBitmapImageRep alloc]
initWithFocusedViewRect:[_previewView visibleRect]];
[_previewView unlockFocus];
NSData *imageData;
NSDictionary *imageProps = [NSDictionary dictionaryWithObject:[NSNumber
numberWithFloat:0.9] forKey:NSImageCompressionFactor];
imageData = [bits representationUsingType:NSJPEGFileType
properties:imageProps];
[imageData writeToFile:#"/Users/buick/Desktop/1.png" atomically:YES];
}
#end

OK I think I got it. Try setting your QLPreviewView type to QLPreviewViewStyleCompact when you init it.
pv = [[QLPreviewView alloc] initWithFrame:pv.bounds
style:QLPreviewViewStyleCompact];
Then use this category
NSImage+QuickLook from Matt Gemmel...
github
NSImage *image = [NSImage imageWithPreviewOfFileAtPath:[[self.files objectAtIndex:i] path] ofSize:NSMakeSize(1200, 1920) asIcon:NO];

Related

Cocoa App hangs when using NSTextView with NSScrollView

I am reading some file content and trying to display on NSTextView with NSScrollView.
My app hangs when I try to display the content of a very large file. The actual scenario is:
open the file.
Read its content in NSData and convert it to NSString.
Create a NSScrollView.
Create a NSTextView.
Set the above created NSString to NSTextView.
File Size is extremely large . It contains around 106010441 lines.
My app is successfully able to show the inital content of the file, until I scroll down it starts hanging and become very slow.
If I click on text area and press command + a to select all and start scrolling, it completely freezes.
I dont know , what I am doing wrong.
Here is my ViewController.m content:
#import "ViewController.h"
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
/
NSString * path = Path_to_file;
NSFileHandle * fileHandle = [NSFileHandle fileHandleForReadingAtPath:path];
NSData * buffer = [fileHandle readDataToEndOfFile];
raw_string = [[NSString alloc] initWithBytes:[buffer bytes] length:[buffer length] encoding:NSUTF8StringEncoding];
}
- (void)setRepresentedObject:(id)representedObject {
[super setRepresentedObject:representedObject];
}
- (IBAction)button:(id)sender {
_Scroller.hasVerticalScroller = YES;
_Scroller.hasHorizontalScroller = YES;
_Scroller.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
NSSize contentSize = _Scroller.contentSize;
NSTextView* _textView = [[NSTextView alloc] initWithFrame:NSMakeRect(0, 0,
contentSize.width, contentSize.height)];
_textView.minSize = NSMakeSize(0.0, contentSize.height);
_textView.maxSize = NSMakeSize(FLT_MAX, FLT_MAX);
_textView.verticallyResizable = YES;
_textView.horizontallyResizable = YES;
_textView.autoresizingMask = NSViewWidthSizable;
_textView.textContainer.containerSize = NSMakeSize(contentSize.width, FLT_MAX);
_textView.textContainer.widthTracksTextView = YES;
_textView.automaticSpellingCorrectionEnabled = NO;
_textView.continuousSpellCheckingEnabled = NO;
_Scroller.documentView = _textView;
_textView.string = raw_string;
}
#end

Objective-C: Uploading too many images memory pressure causing app to quit

I am using QBImagePicker to allow multiple image upload. It works fine for up to 25 images being downloaded, but more than that, and the app will quit do to memory pressure while uploading. I would like to allow infinite image upload, and am uncertain how to do so where memory would not be an issue (i.e. perhaps clearing memory after each save). Here is my method to save images (which is called from a loop within the main QBImagePickerController method to save all the selected images):
- (void) saveTheImage:(UIImage *)image fileName:(NSString *)name width:(CGFloat) width height:(CGFloat) height quality:(CGFloat) quality extension:(int)fileNumberExtension
{
UIImage *resizedImage = [self resizeImage:image width:width height:height]; //this is a simple method I have to resize the image sent from the picker
NSData *data = UIImageJPEGRepresentation(resizedImage, quality); //save as a jpeg
NSString *fileName = [NSString stringWithFormat:#"%#%d", name, fileNumberExtension]; //set the filename
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0]; //will be saved in documents
NSString *tempPath = [documentsDirectory stringByAppendingPathComponent:fileName]; //with the filename given
//create a block operation to save
NSBlockOperation* saveOp = [NSBlockOperation blockOperationWithBlock: ^{
[data writeToFile:tempPath atomically:YES];
}];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:saveOp];
}
Thanks in advance!
EDIT
My method to resize the image:
- (UIImage *) resizeImage:(UIImage *)image width:(CGFloat) width height:(CGFloat) height
{
UIImage *resizedImage;
CGSize size = CGSizeMake(width, height);
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0f);
[image drawInRect:CGRectMake(0, 0, width, height)];
resizedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return resizedImage;
}
EDIT 2
Additional methods:
- (void) imagePickerController:(QBImagePickerController *)imagePickerController didSelectAssets:(NSArray *)assets
{
for (int i=0;i<assets.count;i++)
{
ALAssetRepresentation *rep = [[assets objectAtIndex:i] defaultRepresentation];
CGImageRef iref = [rep fullResolutionImage];
UIImage *pickedImage = [UIImage imageWithCGImage:iref scale:[rep scale] orientation:(UIImageOrientation)[rep orientation]];
int fileNumberExtension = [self getHighestImageNumber] + 1; //new images all have a higher file name
//set the ratio (width of image is 294)
CGFloat ratio = pickedImage.size.width / 294;
CGFloat newHeight = pickedImage.size.height / ratio;
if (newHeight < 430) //image is too wide
{
[self saveTheImage:pickedImage fileName:#"img" width:294 height:newHeight quality:0.8f extension:fileNumberExtension];
}
else //if the image is too narrow
{
//set the ratio (height of image is 430)
CGFloat ratio = pickedImage.size.height / 430;
CGFloat newWidth = pickedImage.size.width / ratio;
[self saveTheImage:pickedImage fileName:#"img" width:newWidth height:430 quality:0.8f extension:fileNumberExtension];
}
[self saveTheImage:pickedImage fileName:#"thm" width:78 height:78 quality:0.0f extension:fileNumberExtension]; //save the thumbnail
}
[self dismissImagePickerController];
}
- (void)dismissImagePickerController
{
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void) addImageClicked
{
QBImagePickerController *imagePickerController = [[QBImagePickerController alloc] init];
imagePickerController.delegate = self;
imagePickerController.allowsMultipleSelection = YES;
imagePickerController.maximumNumberOfSelection = 20; //allow up to 20 photos at once
imagePickerController.filterType = QBImagePickerControllerFilterTypePhotos;
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:imagePickerController];
[self presentViewController:navigationController animated:YES completion:nil];
}
Solved this issue by adding by using #autoreleasepool around my for loop in this method:
- (void) imagePickerController:(QBImagePickerController *)imagePickerController didSelectAssets:(NSArray *)assets
This thread was very useful.
You have a memory leak. Leaks usually don't happen because ARC takes care of it for you. (every time you finish using an image, it gets cleared from memory). However, NOT ALL objects are governed by ARC. There are some object types (like CGColorSpaceRef, etc.) that need to be freed manually.
You can check this by running Static Analysis in Xcode. In the top menu bar, select Product -> Analyze. If there are places where you need to free your objects, it will tell you.
To free an object, do:
CGColorSpaceRelease(ref); //where ref is a CGColorSpaceRef.
CGImageRelease(iref); //where iref is a CGImageRef.
or the corresponding method that pertains to your object.

NSButton with CALayer flips the image when clicked

I've got an NSButton with an attached CALayer and an image set on it. When I click the button, the image flips!
_zoomButton = [NSButton new];
[_zoomButton setBordered:NO];
[_zoomButton setButtonType:NSMomentaryChangeButton];
[_zoomButton setTitle:#""];
[_zoomButton setWantsLayer:YES];
[_zoomButton setImage:s_zoomImage];
[_zoomButton sizeToFit];
s_zoomImage is just an NSImage loaded from resources:
namespace
{
NSImage *s_zoomImage; // No static variables in Objective-C.
}
+ (void)initialize
{
[super initialize];
NSString *const zoomImagePath = [[NSBundle mainBundle] pathForResource:#"Zoom.png" ofType:nil];
s_zoomImage = [[[NSImage alloc] initWithContentsOfFile:zoomImagePath] autorelease];
}
At first the button displays correctly, but if I click it, the image (s_zoomImage) flips vertically! Any ideas?

UIWebView loads HTML from local file but fails occasionally

I've come across many ways to load and reload the content of a UIWebView from a local html file but it seems like the issue I have keeps coming back.
On my project I have a webView that dynamically loads HTML content pulled from one of the 50 different HTML files I have in my XCode project.
Depending on which button the user presses on the viewController before, a different HTML file is used to reload the content of my webView. Everything works fine most of the time but every now and then, the webView will simply display "(null)", after the webViewDidFinishLoad delegate method is called.
I don't understand what is going on, and when I go back and forth on this viewController and the webView is reloaded the HTML content ends up showing up properly eventually for the same HTML file.
Here is the code to load the HTML in the webView (called from the viewWillAppear method) :
- (void) reloadWebViewFromLocalFile{
// Reset the UIWebView
[self.contentWebView removeFromSuperview];
self.contentWebView = nil;
self.contentWebView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 1)];
[self.contentWebView setContentMode:UIViewContentModeScaleAspectFit];
[self.contentWebView setDelegate:self];
[self.contentScrollView addSubview:self.contentWebView];
[self.contentWebView release];
NSString *fileName = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"%d.%d",self.currentIndexPath.section+1,self.currentIndexPath.row+1] ofType:#"html"];
NSString *CSSContent = [NSString stringWithFormat:#"<style type=\"text/css\">body{padding-left:8px;padding-right:8px;font-family:\"%#\";}p{text-align:justify;}img{max-width:300px;display:block; margin:auto;}strong{font-family:\"%#\";}h1{font-size:20;font-family:\"%#\"}h2{font-size:18;font-family:\"%#\"}h3{font-size:17;font-family:\"%#\"}</style><script type=\"text/javascript\">touchMove = function(event) {event.preventDefault();}</script>", FONT_STAG_BOOK, FONT_STAG_SEMIBOLD, FONT_STAG_SEMIBOLD, FONT_STAG_SEMIBOLD, FONT_STAG_SEMIBOLD];
self.HTMLString = [NSString stringWithFormat:#"<html><head>%#</head>%#</html>",CSSContent,[NSString stringWithUTF8String:[[NSData dataWithContentsOfFile:fileName] bytes]]];
[self.contentWebView loadHTMLString:self.HTMLString baseURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]]];
}
and this is the webViewDidFinishLoad delegate method :
- (void)webViewDidFinishLoad:(UIWebView *)webView{
NSString *stringContent = [self.contentWebView stringByEvaluatingJavaScriptFromString:#"document.body.innerHTML"];
float height = [[self.contentWebView stringByEvaluatingJavaScriptFromString:#"document.body.offsetHeight;"] floatValue] + 20;
[self.contentWebView setFrame:CGRectMake(0, 0, 320, height)];
[self.contentScrollView setContentSize:CGSizeMake(0, height+self.nextButton.frame.size.height)];
[self.nextButton setFrame:CGRectMake(0, height, 320, self.nextButton.frame.size.height)];
if ([self.contentWebView respondsToSelector:#selector(scrollView)]) {
self.contentWebView.scrollView.scrollEnabled = NO; // available starting in iOS 5
} else {
for (id subview in self.contentWebView .subviews)
if ([[subview class] isSubclassOfClass: [UIScrollView class]])
((UIScrollView *)subview).scrollEnabled = NO;
}
}
Edit : Forgot to copy-paste the line that actually loads the HTML string in the webView
The problem seems to be using the bytes function, which doesn't work too well if the content encoding isn't exact...
[NSString stringWithUTF8String:[[NSData dataWithContentsOfFile:fileName] bytes]];
should be
[[NSString alloc] initWithData:[NSData dataWithContentsOfFile:fileName] encoding:NSUTF8StringEncoding];

Saving entire NSView contents, which is inside an NSScrollView

I'm trying to save contents of an NSView as an image, but, only the image visible inside the scrollview is saved.
Here is the actual image loaded in the view:
.
But this is how I'm able to save the image:
Is there any way to save the entire image in the NSView?
This is how I'm saving my NSView subclass:
- (void)save
{
[self lockFocus];
NSBitmapImageRep* rep = [[NSBitmapImageRep alloc] initWithFocusedViewRect:[self bounds]];
[self unlockFocus];
NSData* data = [rep representationUsingType:NSPNGFileType properties:nil];
NSString* string = NSHomeDirectory();
NSString* pth1 = [string stringByAppendingPathComponent:#"ttx.png"];
[data writeToFile:pth1 atomically:YES];
}
This method is in the View i want to save.
Use -[NSView cacheDisplayInRect:toBitmapImageRep:]
NSBitmapImageRep* rep = [self bitmapImageRepForCachingDisplayInRect:self.bounds];
[self cacheDisplayInRect:self.bounds toBitmapImageRep:rep];