iMessage Apps - Programatically create animated sticker from .apng - ios

Hi has anyone tried yet to programatically create a custom sticker programmatically with an animated apng file?
I've had no success but can state the following:
.apng file of correct dimensions and file size (300px * 300px) and < 500kb.
Can add this as a .png and it shows the first frame (not animated)
Changing file name to .apng causes an error to be raised upon inserting sticker to MS Message.
Have taken the approach of loading the apng into the project folder (not in Assets.xcassets) as the initialize sticker requires an NSURL which is not made availabe if apng is created as an iOS sticker.
Managed to user the same sequence in .GIF. this inserted correctly as a sticker.
Code below:
MSConversation *currentConversation = [self activeConversation];
NSURL *urlForSticker2 = [[NSBundle mainBundle] URLForResource:#"elephant_apng" withExtension:#"apng"];
MSSticker *challengeSticker2 = [[MSSticker alloc] initWithContentsOfFileURL:urlForSticker2 localizedDescription:#"My Sticker Localised" error:&myError];
[currentConversation insertSticker:challengeSticker2 completionHandler:^(NSError * error)
{
//HERE we get: error NSError * domain: #"com.apple.messages.stickers-error" - code: 8 0x0000600000058240
}];
So yes - keen to be able to overcome the error shown above.

Maybe you forget click here when you add the resource?
and your apng file's extension should be just "png" when you insert a MSSticker object.

Related

Screenshot Safari from Share Extension

Is it possible to perform a screenshot of the current visible zone of the webview in Safari from a Share Extension? I could use windows, but UIApplication isn't supported on extensions so I can't access to that window.
You can't since UIApplication can't be reached from an extension. You cannot get the first UIWindow, which is the Safari layer, so you have to play with the Javascript preprocessing file that the extensions have. So just create a Javascript file that, when sent to Safari, generates a base64 string with the current visible zone image data. Take that string through the kUTTypePropertyList identifier in your extension. Since that should be NSData, generate the UIImage from there, by using +imageWithData. That is what you're looking for, without having to load the page again, preventing a second load and a bad image if the webpage requires of a login.
As far as I know, you can't unless you invoke the API you need dynamically, and even so you might run into context permission issues and app store approval issues.
An alternative might be passing the current Safari URL to your extension, load it using a hidden UIWebView and render this view into an UIImage but you will loose the current visible zone information...
Edit: So the below works in the Simulator but does not work on the device. I'm presently looking for a solution as well.
You can't get just the visible area of Safari, but you can get a screenshot with a little ingenuity. The following method captures a screenshot from a ShareViewController.
func captureScreen() -> UIImage
{
// Get the "screenshot" view.
let view = UIScreen.mainScreen().snapshotViewAfterScreenUpdates(false)
// Add the screenshot view as a subview of the ShareViewController's view.
self.view.addSubview(view);
// Now screenshot *this* view.
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, false, 0);
self.view.drawViewHierarchyInRect(view.bounds, afterScreenUpdates: true)
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// Finally, remove the subview.
view.removeFromSuperview()
return image
}
This is the approved way to capture the screenshot of a webpage in a share extension:
for (NSExtensionItem *item in self.extensionContext.inputItems) {
for (NSItemProvider *itemProvider in item.attachments) {
[itemProvider loadPreviewImageWithOptions:#{NSItemProviderPreferredImageSizeKey: [NSValue valueWithCGSize:CGSizeMake(60.0f, 60.0f)]} completionHandler:^(UIImage * item, NSError * _Null_unspecified error) {
// Set the size to that desired, however,
// Note that the image 'item' returns will not necessarily by the size that you requested, so code should handle that case.
// Use the UIImage however you wish here.
}];
}
}

UIImageView+animatedGIF always LOOPS

I used class made by "mayoff" (Rob Mayoff) "UIImageView+animatedGIF" which was proposed in one of the answers here on stackoverflow. UIImageView+animatedGIF
With it I can import animated .gif images in UIImageView on iOS. This class works flawlessly, but the only problem is that .gif is always looping. No matter how I export it (I am exporting image from photoshop - 67 frames, set repeat to "once") it loops forever in UIImageView.
I am importing my .gif with these two lines:
NSURL *url = [[NSBundle mainBundle] URLForResource:#"Loading" withExtension:#"gif"];
self.loadingImageView.image = [UIImage animatedImageWithAnimatedGIFData:[NSData dataWithContentsOfURL:url]];
very simple and it works.
I have tried setting animationRepeatCount property to 1, as recomended by Rob but that didn't do the trick. Also I have tried setting the UIImageView's animationImages property to
gifImage.images too, instead of just setting the view's image property
to gifImage. In that case, .gif is not animating at all.
Any ideas how to play that .gif only once? I can think of many tricks (like setting last frame of the gif to show up after some time but I'd first try something simpler if possible).
I took the test app from that project and changed the viewDidLoad method to this:
- (void)viewDidLoad {
[super viewDidLoad];
NSURL *url = [[NSBundle mainBundle] URLForResource:#"test" withExtension:#"gif"];
UIImage *testImage = [UIImage animatedImageWithAnimatedGIFData:[NSData dataWithContentsOfURL:url]];
self.dataImageView.animationImages = testImage.images;
self.dataImageView.animationDuration = testImage.duration;
self.dataImageView.animationRepeatCount = 1;
self.dataImageView.image = testImage.images.lastObject;
[self.dataImageView startAnimating];
}
When the app launches, it animates the image once and then shows just the last frame. If I connect a button that sends [self.dataImageView startAnimating], then tapping the button runs the animation one more time, then stops again on the last frame.
Make sure you're setting your image view up the way I do above. Make sure you are not setting the image view's image property to the animated image. The image view's image property must be set to a non-animated image if you want the animation to stop.
I have forked "uiimage-from-animated-gif" made by "Dreddik" and added a delegate method
#pragma mark - AnimatedGifDelegate
- (void)animationWillRepeat:(AnimatedGif *)animatedGif
{
NSLog(#"animationWillRepeat");
//[animatedGif stop];
}
So you know the time when animation will repeat and do your own thing in middle of every repetition. You may count the number of times animation repeated and stop animation at a certain number reached.
The forked repository is at https://github.com/rishi420/Animated-GIF-iPhone
Was having issues in start/stp animation & managing animation count
So used this pod
imageViewObject.animate(withGIFNamed: "gif_name", loopCount: 1) {
print("animating image")
}
To start/stop animation :
imageViewObject.isAnimatingGIF ? imageViewObject.stopAnimatingGIF() : imageViewObject.startAnimatingGIF()

IOS opening PDF

I am trying to open PDF file into my application with a custom UIBarButtonItem into the UINavigationBar, two options to open pdf into IOS
-UIDocumentInteractionController
-QLPreviewController
in IOS6 i have found this posts with prove that we cant customize the UINavigationBar
Custom navigationItem button with QLPreviewController in iOS6
QLPreviewController remove or add UIBarButtonItems
http://www.cimgf.com/2012/07/11/a-better-fullscreen-asset-viewer-with-quicklook/
Custom "Email" action in UIDocumentInteractionController
"iOS6 Update This technique of overriding the QLPreviewController will no longer work in iOS 6. I have contacted Apple about the situation, but they simply stated that it is no longer supported and it is considered a private API"
is there is any other way to open PDF in IOS without Using UIWebView and to customize the UIBarButtonItem.
I don't know what exactly you want to do with the PDF, but you can open a PDF file from the bundle using some code like this:
CGPDFDocumentRef pdfDoc;
CGPDFPageRef pdfPage;
NSURL* pdfURL = [[NSBundle mainBundle] URLForResource:#"filename" withExtension:#"pdf"];
CFURLRef urlRef = (__bridge CFURLRef)pdfURL;
pdfDoc = CGPDFDocumentCreateWithURL(urlRef);
if (pdfDoc == NULL) {
// Not good
}
if (CGPDFDocumentIsEncrypted (pdfDoc)) {
if (!CGPDFDocumentUnlockWithPassword (pdfDoc, "")) {
if (!CGPDFDocumentUnlockWithPassword (pdfDoc, "password")) {
// Not good, document is locked
}
}
}
if (!CGPDFDocumentIsUnlocked (pdfDoc)) {
CGPDFDocumentRelease(pdfDoc);
} else {
pdfPage = CGPDFDocumentGetPage(pdfDoc, 1);
CGPDFPageRetain(pdfPage);
// ...
}
From this point on the documentations from Apple should help you:
Quartz 2D Programming Guide
Core Graphics Framework Reference

Memory Leak Downloading image from server

I have a paged slider view with an image on each page. I'm using NSOperationQueue to help me download the images from the server while the program is running. The NSOperationQueue is used to call the following method,
-(NSData *)imageWith:(NSString *)imageName
{
NSString *imagePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:imageName];
NSData *imageData = [NSData dataWithContentsOfFile:imagePath];
if (!imageData) {
imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:[[NSString stringWithFormat:#"%#/%#", picsURL,imageName] stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding]]];
if (imageData) {
[imageData writeToFile:imagePath atomically:YES];
}
}
return imageData;
}
and then I use the main thread to display the downloaded image on the scrollerview:
[self performSelectorOnMainThread:#selector(loadPic:) withObject:[NSArray arrayWithObjects:[self imageWith:[picsNames objectAtIndex:imageView.tag]], [NSString stringWithFormat:#"%d", imageView.tag], nil] waitUntilDone:YES];
which calls the following method:
-(void)loadPic:(NSArray *)imageAndTagArray
{
if (imageAndTagArray.count) {
//loading the image to imageview
UIImageView *imageView = (UIImageView *)[scrollView viewWithTag:[[imageAndTagArray objectAtIndex:1] intValue]];
imageView.image = [UIImage imageWithData:((NSData *)[imageAndTagArray objectAtIndex:0])];
//stopping the indicator
[((UIActivityIndicatorView *)[imageView viewWithTag:ACTIVITY_INDICATOR_TAG]) stopAnimating];
}
}
Everything works fine for the first 60 images, but after that I receive a Memory Warning and after about 100 images the app crashes.
I have been spending so much time on this and I can't figure out what to do. I've used Instruments and it doesn't detect any leak. I've also used Analyze and that did show anything either.
EDIT:
If I replace the imageWith: method definition with the following definition I still get the warnings, where 5.jpg is a local image.
-(NSData *)imageWith:(NSString *)imageName
{
return UIImagePNGRepresentation([UIImage imageNamed:#"5.jpg"]);
}
Let me tell you more about the situation.
When the app starts I have a view with a paged scrollview inside it that contains 9 images per page. the scrollview uses the nsoperationqueue to load images which calls the imageWith: method.
when the user taps on any of the images a second view opens with a full display of the selected image. this second view also has a scroll view that contains the same images as the first view but with full display, meaning 1 image per page.
when you are on the second view and scrolling back and forth the app crashes after loading about 60 images.
It also crashes if say it loads 50 images and then you tap on the back button and go to the first view and then tap on another image and go to the second view and load about 10 images.
It sounds like you're holding too many images in memory. When you open the second view, it's reloading the images again from disk, until you end up with two copies of all the images.
The UIImage class may be able to help you with this memory management. In its reference page, it mentions that it has the capability to purge its data in low-memory situations and then reload the file from disk when it needs to be drawn again. This might be your solution.
However, as you're creating the image from an NSData read from disk, the UIImage will probably not be able to purge its memory - it won't know that your image is simply stored on the disk, so it can't throw away the data and reload it later.
Try changing your "imageWith" method to create a UIImage (via imageWithContentsOfFile) from the file URL on the disk just before it returns, and return the UIImage rather than returning the intermediate NSData. That way, the UIImage will know where on disk its image source came from and be able to intelligently purge/reload it as memory becomes constrained on the device.

Trouble Displaying Images On Device

I'm working on my first IOS app. The app downloads and displays data from a database via a PHP web page. That's all working fine. I also grab an image from the same web server to display in a UIImageView. This all works on fine on the Simulator.
On my test device (a 3GS), everything works except I cannot get the downloaded image to display in my UIImageView.
If there is no internet connection, I am able to display my alternate image that I've included in app's bundle on the simulator and on the device.
// Inside My data class Implementation
- (void)setUpTheData
{
--- other code ---
NSURL *myImageURL = [NSURL URLWithString:myImageURLString];
NSData *myImageRawData = [NSData dataWithContentsOfURL:myImageURL];
self.myImageData = myImageRawData;
}
- (NSData*)getTheImageData
{
return _myImageData;
}
// Inside my viewDidLoad
UIImage *theImage = [UIImage imageWithData:[theRemoteData getTheImageData]];
_testImage.image = theImage;
I've compared the image data from both the simulator and the device and they are the same.
Try viewDidAppear instead of viewDidLoad maybe?
I had set a weak pointer in my imageData property in my data class. Changing to strong fixed my problem.

Resources