How to save Image from UIWebView in Objective C [duplicate] - ios

Is it just me or has the action sheet on <img> tags been disabled in UIWebView? In Safari, e.g, when you want to save an image locally, you touch and hold on the image to get an action sheet shown. But it's not working in my custom UIWebView. I mean, it is still working for <a> tags, i.e, when I touch and hold on html links, an action sheet shows up. But not for the <img> tags.
I've tried things like putting img { -webkit-touch-callout: inherit; } in css, which didn't work. On the other hand, when I double-tap and hold on the images, a copy-balloon shows up.
So the question is, has the default action sheet callout for <img> tags been disabled for UIWebView? Is so, is there a way to re-enable it? I've googled around and saw many Q&As on how to disable it in UIWebView, so is it just me who aren't seeing the popup?
Thanks in advance!

Yes apple has disabled this feature (among others) in UIWebViews and kept it for Safari only.
However you can recreate this yourself by extending this tutorial, http://www.icab.de/blog/2010/07/11/customize-the-contextual-menu-of-uiwebview/.
Once you've finished this tutorial you'll want to add a few extra's so you can actually save images (which the tutorial doesn't cover).
I added an extra notification called #"tapAndHoldShortNotification" after 0.3 seconds which calls a method with just the disable callout code in it (to prevent both the default and your own menu popping while the page is still loading, a little bug fix).
Also to detect images you'll need to extend the JSTools.js, here's mine with the extra functions.
function MyAppGetHTMLElementsAtPoint(x,y) {
var tags = ",";
var e = document.elementFromPoint(x,y);
while (e) {
if (e.tagName) {
tags += e.tagName + ',';
}
e = e.parentNode;
}
return tags;
}
function MyAppGetLinkSRCAtPoint(x,y) {
var tags = "";
var e = document.elementFromPoint(x,y);
while (e) {
if (e.src) {
tags += e.src;
break;
}
e = e.parentNode;
}
return tags;
}
function MyAppGetLinkHREFAtPoint(x,y) {
var tags = "";
var e = document.elementFromPoint(x,y);
while (e) {
if (e.href) {
tags += e.href;
break;
}
e = e.parentNode;
}
return tags;
}
Now you can detect the user clicking on images and actually find out the images url they are clicking on, but we need to change the -(void)openContextualMenuAtPoint: method to provide extra options.
Again here's mine (I tried to copy Safari's behaviour for this):
- (void)openContextualMenuAt:(CGPoint)pt{
// Load the JavaScript code from the Resources and inject it into the web page
NSString *path = [[NSBundle mainBundle] pathForResource:#"JSTools" ofType:#"js"];
NSString *jsCode = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
[webView stringByEvaluatingJavaScriptFromString:jsCode];
// get the Tags at the touch location
NSString *tags = [webView stringByEvaluatingJavaScriptFromString:
[NSString stringWithFormat:#"MyAppGetHTMLElementsAtPoint(%i,%i);",(NSInteger)pt.x,(NSInteger)pt.y]];
NSString *tagsHREF = [webView stringByEvaluatingJavaScriptFromString:
[NSString stringWithFormat:#"MyAppGetLinkHREFAtPoint(%i,%i);",(NSInteger)pt.x,(NSInteger)pt.y]];
NSString *tagsSRC = [webView stringByEvaluatingJavaScriptFromString:
[NSString stringWithFormat:#"MyAppGetLinkSRCAtPoint(%i,%i);",(NSInteger)pt.x,(NSInteger)pt.y]];
UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:nil delegate:self cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles:nil];
selectedLinkURL = #"";
selectedImageURL = #"";
// If an image was touched, add image-related buttons.
if ([tags rangeOfString:#",IMG,"].location != NSNotFound) {
selectedImageURL = tagsSRC;
if (sheet.title == nil) {
sheet.title = tagsSRC;
}
[sheet addButtonWithTitle:#"Save Image"];
[sheet addButtonWithTitle:#"Copy Image"];
}
// If a link is pressed add image buttons.
if ([tags rangeOfString:#",A,"].location != NSNotFound){
selectedLinkURL = tagsHREF;
sheet.title = tagsHREF;
[sheet addButtonWithTitle:#"Open"];
[sheet addButtonWithTitle:#"Copy"];
}
if (sheet.numberOfButtons > 0) {
[sheet addButtonWithTitle:#"Cancel"];
sheet.cancelButtonIndex = (sheet.numberOfButtons-1);
[sheet showInView:webView];
}
[selectedLinkURL retain];
[selectedImageURL retain];
[sheet release];
}
(NOTES: selectedLinkURL and selectedImageURL are declared in the .h file to let them be accessed throughout the class, for saving or opening the link latter.
So far we've just been going back over the tutorials code making changes but now we will move into what the tutorial doesn't cover (it stops before actually mentioning how to handle saving the images or opening the links).
To handle the users choice we now need to add the actionSheet:clickedButtonAtIndex: method.
-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex{
if ([[actionSheet buttonTitleAtIndex:buttonIndex] isEqualToString:#"Open"]){
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:selectedLinkURL]]];
}
else if ([[actionSheet buttonTitleAtIndex:buttonIndex] isEqualToString:#"Copy"]){
[[UIPasteboard generalPasteboard] setString:selectedLinkURL];
}
else if ([[actionSheet buttonTitleAtIndex:buttonIndex] isEqualToString:#"Copy Image"]){
[[UIPasteboard generalPasteboard] setString:selectedImageURL];
}
else if ([[actionSheet buttonTitleAtIndex:buttonIndex] isEqualToString:#"Save Image"]){
NSOperationQueue *queue = [NSOperationQueue new];
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(saveImageURL:) object:selectedImageURL];
[queue addOperation:operation];
[operation release];
}
}
This checks what the user wants to do and handles /most/ of them, only the "save image" operation needs another method to handle that. For the progress I used MBProgressHub.
Add an MBProgressHUB *progressHud; to the interface declaration in the .h and set it up in the init method (of whatever class you're handling the webview from).
progressHud = [[MBProgressHUD alloc] initWithView:self.view];
progressHud.customView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Tick.png"]] autorelease];
progressHud.opacity = 0.8;
[self.view addSubview:progressHud];
[progressHud hide:NO];
progressHud.userInteractionEnabled = NO;
And the -(void)saveImageURL:(NSString*)url; method will actually save it to the image library.
(A better way would be to do the download through an NSURLRequest and update the progress hud in MBProgressHUDModeDeterminate to deflect how long it'll actually take to download, but this is a more hacked together implementation then that)
-(void)saveImageURL:(NSString*)url{
[self performSelectorOnMainThread:#selector(showStartSaveAlert) withObject:nil waitUntilDone:YES];
UIImageWriteToSavedPhotosAlbum([UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:url]]], nil, nil, nil);
[self performSelectorOnMainThread:#selector(showFinishedSaveAlert) withObject:nil waitUntilDone:YES];
}
-(void)showStartSaveAlert{
progressHud.mode = MBProgressHUDModeIndeterminate;
progressHud.labelText = #"Saving Image...";
[progressHud show:YES];
}
-(void)showFinishedSaveAlert{
// Set custom view mode
progressHud.mode = MBProgressHUDModeCustomView;
progressHud.labelText = #"Completed";
[progressHud performSelector:#selector(hide:) withObject:[NSNumber numberWithBool:YES] afterDelay:0.5];
}
And of cause add [progressHud release]; to the dealloc method.
Hopefully this shows you how to add some of the options to a webView that apple left out.
Of cause though you can add more things to this like a "Read Later" option for instapaper or a "Open In Safari" button.
(looking at the length of this post I'm seeing why the original tutorial left out the finial implementation details)
Edit: (updated with more info)
I was asked about the detail I glossed over at the top, the #"tapAndHoldShortNotification", so this is clarifying it.
This is my UIWindow subclass, it adds the second notification to cancel the default selection menu (this is because when I tried the tutorial it showed both menus).
- (void)tapAndHoldAction:(NSTimer*)timer {
contextualMenuTimer = nil;
UIView* clickedView = [self hitTest:CGPointMake(tapLocation.x, tapLocation.y) withEvent:nil];
while (clickedView != nil) {
if ([clickedView isKindOfClass:[UIWebView class]]) {
break;
}
clickedView = clickedView.superview;
}
if (clickedView) {
NSDictionary *coord = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat:tapLocation.x],#"x",
[NSNumber numberWithFloat:tapLocation.y],#"y",nil];
[[NSNotificationCenter defaultCenter] postNotificationName:#"TapAndHoldNotification" object:coord];
}
}
- (void)tapAndHoldActionShort:(NSTimer*)timer {
UIView* clickedView = [self hitTest:CGPointMake(tapLocation.x, tapLocation.y) withEvent:nil];
while (clickedView != nil) {
if ([clickedView isKindOfClass:[UIWebView class]]) {
break;
}
clickedView = clickedView.superview;
}
if (clickedView) {
NSDictionary *coord = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat:tapLocation.x],#"x",
[NSNumber numberWithFloat:tapLocation.y],#"y",nil];
[[NSNotificationCenter defaultCenter] postNotificationName:#"TapAndHoldShortNotification" object:coord];
}
}
- (void)sendEvent:(UIEvent *)event {
NSSet *touches = [event touchesForWindow:self];
[touches retain];
[super sendEvent:event]; // Call super to make sure the event is processed as usual
if ([touches count] == 1) { // We're only interested in one-finger events
UITouch *touch = [touches anyObject];
switch ([touch phase]) {
case UITouchPhaseBegan: // A finger touched the screen
tapLocation = [touch locationInView:self];
[contextualMenuTimer invalidate];
contextualMenuTimer = [NSTimer scheduledTimerWithTimeInterval:0.8 target:self selector:#selector(tapAndHoldAction:) userInfo:nil repeats:NO];
NSTimer *myTimer;
myTimer = [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:#selector(tapAndHoldActionShort:) userInfo:nil repeats:NO];
break;
case UITouchPhaseEnded:
case UITouchPhaseMoved:
case UITouchPhaseCancelled:
[contextualMenuTimer invalidate];
contextualMenuTimer = nil;
break;
}
} else { // Multiple fingers are touching the screen
[contextualMenuTimer invalidate];
contextualMenuTimer = nil;
}
[touches release];
}
The notification is then handled like this:
// in -viewDidLoad
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(stopSelection:) name:#"TapAndHoldShortNotification" object:nil];
- (void)stopSelection:(NSNotification*)notification{
[webView stringByEvaluatingJavaScriptFromString:#"document.documentElement.style.webkitTouchCallout='none';"];
}
It's only a little change but it fixes the annoying little bug where you get 2 menus appear (the standard one and yours).
Also you could easily add iPad support by sending the touches location as the notification fires and then showing the UIActionSheet from that point, though this was written before the iPad so doesn't include support for that.

After struggling for, like 2 or 3 days non-stop on this problem, it seems like the position is computed "relatively" to the UIWebView's "TOP-LEFT" corner (I am programing for iOS 7).
So, to make this work, when you get the position, on the controller where your WebView is (i'll put a snippet of my code below), don't add the "scroll-offset"
SNIPPET - ContextualMenuAction:
- (void)contextualMenuAction:(NSNotification*)notification {
// Load javascript
[self loadJavascript];
// Initialize the coordinates
CGPoint pt;
pt.x = [[[notification object] objectForKey:#"x"] floatValue];
pt.y = [[[notification object] objectForKey:#"y"] floatValue];
// Convert point from window to view coordinate system
pt = [self.WebView convertPoint:pt fromView:nil];
// Get PAGE and UIWEBVIEW dimensions
CGSize pageDimensions = [self.WebView documentSize];
CGSize webviewDimensions = self.WebView.frame.size;
/***** If the page is in MOBILE version *****/
if (webviewDimensions.width == pageDimensions.width) {
}
/***** If the page is in DESKTOP version *****/
else {
// convert point from view to HTML coordinate system
CGSize viewSize = [self.WebView frame].size;
// Contiens la portion de la page visible depuis la webview (en fonction du zoom)
CGSize windowSize = [self.WebView windowSize];
CGFloat factor = windowSize.width / viewSize.width;
CGFloat factorHeight = windowSize.height / viewSize.height;
NSLog(#"factor: %f", factor);
pt.x = pt.x * factor; // ** logically, we would add the offset **
pt.y = pt.y * factorHeight; // ** logically, we would add the offset **
}
NSLog(#"x: %f and y: %f", pt.x, pt.y);
NSLog(#"WINDOW: width: %f height: %f", [self.WebView windowSize].width, [self.WebView windowSize].height);
NSLog(#"DOCUMENT: width: %f height: %f", pageDimensions.width, pageDimensions.height);
[self openContextualMenuAt:pt];
}
SNIPPET - in openContextualMenuAt:
To load the correct JS function:
- (void)openContextualMenuAt:(CGPoint)pt {
// Load javascript
[self loadJavascript];
// get the Tags at the touch location
NSString *tags = [self.WebView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:#"getHTMLTagsAtPoint(%li,%li);",(long)pt.x,(long)pt.y]];
...
}
SNIPPET - in JSTools.js:
This is the function I use to get the element touched
function getHTMLTagsAtPoint(x,y) {
var tags = ",";
var element = document.elementFromPoint(x,y);
while (element) {
if (element.tagName) {
tags += element.tagName + ',';
}
element = element.parentNode;
}
return tags;
}
SNIPPET - loadJavascript
I use this one to inject my JS code in the webview
-(void)loadJavascript {
[self.WebView stringByEvaluatingJavaScriptFromString:
[NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"JSTools" ofType:#"js"] encoding:NSUTF8StringEncoding error:nil]];
}
This part (everything I did to overrride the default UIActionSheet) is HEAVILY (should I say completely) based on
this post
#Freerunning's answer is complete (i did almost everything he said in my other classes, like on the post my code is based on), the snippets i posted is just to show you more "completely" how my code is.
Hope this helps! ^^

First of all thanks to Freerunnering for the great solution!
But you can do this with an UILongPressGestureRecognizer instead of a custom LongPressRecognizer. This makes things a bit easier to implement:
In the Viewcontroller Containing the webView:
Add UIGestureRecognizerDelegate to your ViewController
let mainJavascript = "function MyAppGetHTMLElementsAtPoint(x,y) { var tags = \",\"; var e = document.elementFromPoint(x,y); while (e) { if (e.tagName) { tags += e.tagName + ','; } e = e.parentNode; } return tags; } function MyAppGetLinkSRCAtPoint(x,y) { var tags = \"\"; var e = document.elementFromPoint(x,y); while (e) { if (e.src) { tags += e.src; break; } e = e.parentNode; } return tags; } function MyAppGetLinkHREFAtPoint(x,y) { var tags = \"\"; var e = document.elementFromPoint(x,y); while (e) { if (e.href) { tags += e.href; break; } e = e.parentNode; } return tags; }"
func viewDidLoad() {
...
let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(CustomViewController.longPressRecognizerAction(_:)))
self.webView.scrollView.addGestureRecognizer(longPressRecognizer)
longPressRecognizer.delegate = self
...
}
func longPressRecognizerAction(sender: UILongPressGestureRecognizer) {
if sender.state == UIGestureRecognizerState.Began {
let tapPostion = sender.locationInView(self.webView)
let tags = self.webView.stringByEvaluatingJavaScriptFromString("MyAppGetHTMLElementsAtPoint(\(tapPostion.x),\(tapPostion.y));")
let href = self.webView.stringByEvaluatingJavaScriptFromString("MyAppGetLinkHREFAtPoint(\(tapPostion.x),\(tapPostion.y));")
let src = self.webView.stringByEvaluatingJavaScriptFromString("MyAppGetLinkSRCAtPoint(\(tapPostion.x),\(tapPostion.y));")
print("tags: \(tags)\nhref: \(href)\nsrc: \(src)")
// handle the results, for example with an UIDocumentInteractionController
}
}
// Without this function, the customLongPressRecognizer would be replaced by the original UIWebView LongPressRecognizer
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
And thats it!

Related

How to set UIImageView Size and how to make sure it stays that size all the time?

I've been experimenting on augmented reality with iOS using this library: https://github.com/promet/PRAugmentedReality
I added another xib named "POIDetails" for displaying details of my points of interest. I added a button for returning back to camera screen by this code:
[self dismissViewControllerAnimated:self completion:nil];
But when i return back the AR screen, the POI image gets enormous.
I tried to set height contraint to 50 and aspect to 1:1 But it didn't do any good. I tried to use sizeThatFits like this:
[myPOI sizeThatFits:CGSizeMake(50, 50)];
But it didn't work too. After seeing no difference, I set back everything i did on image's sizing.
Below you can see before and after images and google drive link for downloading my project. How can i make sure this image doesn't get out of 50 px height?
Link: https://drive.google.com/file/d/0B-cDfWHidgvcVVFRNll0OVR2UTA/view?usp=sharing
Here's my ARObject.m file
#import "ARObject.h"
#import "POIDetails.h"
#interface ARObject ()
#end
#implementation ARObject
#synthesize arTitle, distance;
- (id)initWithId:(int)newId
title:(NSString*)newTitle
coordinates:(CLLocationCoordinate2D)newCoordinates
andCurrentLocation:(CLLocationCoordinate2D)currLoc
{
self = [super init];
if (self) {
arId = newId;
arTitle = [[NSString alloc] initWithString:newTitle];
lat = newCoordinates.latitude;
lon = newCoordinates.longitude;
distance = #([self calculateDistanceFrom:currLoc]);
[self.view setTag:newId];
}
return self;
}
-(double)calculateDistanceFrom:(CLLocationCoordinate2D)user_loc_coord
{
CLLocationCoordinate2D object_loc_coord = CLLocationCoordinate2DMake(lat, lon);
CLLocation *object_location = [[CLLocation alloc] initWithLatitude:object_loc_coord.latitude
longitude:object_loc_coord.longitude];
CLLocation *user_location = [[CLLocation alloc] initWithLatitude:user_loc_coord.latitude
longitude:user_loc_coord.longitude];
return [object_location distanceFromLocation:user_location];
}
-(NSString*)getDistanceLabelText
{
if (distance.doubleValue > POINT_ONE_MILE_METERS)
return [NSString stringWithFormat:#"%.2f mi", distance.doubleValue*METERS_TO_MILES];
else return [NSString stringWithFormat:#"%.0f ft", distance.doubleValue*METERS_TO_FEET];
}
- (NSDictionary*)getARObjectData
{
NSArray *keys = #[#"id",#"title", #"latitude", #"longitude", #"distance"];
NSArray *values = #[#(arId),
arTitle,
#(lat),
#(lon),
distance];
return [NSDictionary dictionaryWithObjects:values forKeys:keys];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[titleL setText:arTitle];
[distanceL setText:[self getDistanceLabelText]];
}
-(void)myPOITapping:(UIGestureRecognizer *)recognizer
{
NSLog(#"image click");
POIDetails *poiDetails = [[POIDetails alloc] initWithNibName:#"POIDetails" bundle:nil]; // constructor
[self presentViewController:poiDetails animated:YES completion:nil];
}
// viewDidLoad wasn't in the library, timur added this method
- (void)viewDidLoad {
myPOI.contentMode = UIViewContentModeScaleToFill;
myPOI.translatesAutoresizingMaskIntoConstraints=YES;
[myPOI setUserInteractionEnabled:YES];
UITapGestureRecognizer *myPOITap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(myPOITapping:)];
[myPOITap setNumberOfTapsRequired:1];
[myPOI addGestureRecognizer:myPOITap];
[self.view addSubview:myPOI];
}
#pragma mark -- OO Methods
- (NSString *)description {
return [NSString stringWithFormat:#"ARObject %d - %# - lat: %f - lon: %f - distance: %#",
arId, arTitle, lat, lon, distance];
}
#end
Edit:
I added following codes to my ARObject.m file's viewDidLoad function and It seems to keep image in set resolution.
myPOI.contentMode = UIViewContentModeScaleToFill;
myPOI.translatesAutoresizingMaskIntoConstraints=YES;
But I'm still having issues with my distance label. I set top and bottom spacing constraint to nearest neighbour and label is now half way up but still seperated from my point of interest image like this:
Before:
After:
I tried to set height contraint to 50 and aspect to 1:1 But it didn't do any good
Check if translatesAutoresizingMaskIntoConstraints property of your UIImageView instance is set to NO when you using constraints.
Also check if UIImageView's instance contentMode property set to UIViewContentModeScaleToFill
ARObject.m
CGRect myRect;
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
/*
keep existed in this method code
*/
/*
move [self.view addSubview:myPOI] line to here, leave all other in viewDidLoad
*/
if (myRect.size.height != 0) {self.view.frame = myRect;}
[self.view addSubview:myPOI];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
myRect = self.view.frame;
[myPOI removeFromSuperview];
}
- (void)viewDidLoad
{
myRect = CGRectZero;
/*
all your other code
*/
}
Unfortunately I cant test code, because device required for this project.

Reload UIScrollView by tapping a UIButton

Let me explain my project first. I have some data in my SQLIte DB table called "note".
In "note" table I have these fields: id, noteToken, note.
What I am doing here is load all the note in an NSMUtableArray from that table. And create UIButton according to that array content number and add those buttons in a UIScrollView as subView. The number of buttons and width of scrollview generate auto according to the number of content of that array. Now, when some one tap one of those Buttons, it will bring him to a next viewController and show him the corresponding note details in that viewController.
I do the same thing with another NSMUtableArray, but these time it read all the id from the "note" table. It equally generate new delete button in the same UIScrollView. But if some one tap on these delete button it will delete that particular note from the table "note" of SQLIte DB. AND RELOAD THE UIScrollView. All are done except the RELOAD THE UIScrollView part. This is what I want. I tried with all exist solution but don't know why it's not working.
Here is my code:
- (void)viewDidLoad
{
[super viewDidLoad];
self.noteToken = [NSString stringWithFormat:#"%#%#", fairId, exibitorId];
scrollViewNoteWidth = 100;
[scrollViewNote setScrollEnabled:YES];
[scrollViewNote setContentSize:CGSizeMake((noteButtonWidth * countNoteButtonArray) + scrollViewNoteWidth, 100)];
sqLite = [[SQLite alloc] init];
[self.sqLite callDataBaseAndNoteTableMethods];
self.noteButtonArrayy = [[NSMutableArray alloc] init];
noteButtonArrayy = [self.sqLite returnDataFromNoteTable:noteToken];
[self LoadNoteButtonAndDeleteButton:noteButtonArrayy];
}
//////////////*----------------------- Note Section (Down) -----------------------*//////////////
-(void) LoadNoteButtonAndDeleteButton:(NSMutableArray *) noteButtonArray
{
sQLiteClass = [[SQLiteClass alloc] init];
noteButtonArrayToShowNoteButton = [[NSMutableArray alloc] init];
/*--------------- Load the noteButton & pass note (Down)---------------*/
for (int i = 0; i < [noteButtonArray count]; i++)
{
sQLiteClass = [noteButtonArray objectAtIndex:i];
// NSString *ids = [NSString stringWithFormat:#"%d", sQLiteClass.idNum];
NSString *nt = sQLiteClass.note;
[noteButtonArrayToShowNoteButton addObject:nt];
}
[self ShowNoteButtonMethod:noteButtonArrayToShowNoteButton];
/*--------------- Load the noteButton & pass note (Up)---------------*/
/*--------------- Load the deleteButton & pass id (Down)---------------*/
noteButtonArrayToDeleteNoteButton = [[NSMutableArray alloc] init];
for (int i = 0; i < [noteButtonArray count]; i++)
{
sQLiteClass = [noteButtonArray objectAtIndex:i];
// Convert int into NSString
NSString *ids = [NSString stringWithFormat:#"%d", sQLiteClass.idNum];
[noteButtonArrayToDeleteNoteButton addObject:ids];
}
[self ShowNoteDeleteButtonMethod:noteButtonArrayToDeleteNoteButton];
/*--------------- Load the deleteButton & pass id (Down)---------------*/
}
-(void) ShowNoteButtonMethod:(NSMutableArray *) btnarray
{
countNoteButtonArray = [btnarray count];
// For note button
noteButtonWidth = 60;
noteButtonXposition = 8;
for (NSString *urls in btnarray)
{
noteButtonXposition = [self addNoteButton:noteButtonXposition AndURL:urls];
}
}
-(int) addNoteButton:(int) xposition AndURL:(NSString *) urls
{
noteButton =[ButtonClass buttonWithType:UIButtonTypeCustom];
noteButton.frame = CGRectMake(noteButtonXposition, 8.0, noteButtonWidth, 60.0);
[noteButton setImage:[UIImage imageNamed:#"note.png"] forState:UIControlStateNormal];
[noteButton addTarget:self action:#selector(tapOnNoteButton:) forControlEvents:UIControlEventTouchUpInside];
[noteButton setUrl:urls];
noteButton.backgroundColor = [UIColor clearColor];
[self.scrollViewNote addSubview:noteButton];
noteButtonXposition = noteButtonXposition + noteButtonWidth + 18;
return noteButtonXposition;
}
-(void)tapOnNoteButton:(ButtonClass*)sender
{
urlNote = sender.url;
[self performSegueWithIdentifier:#"goToNoteDetailsViewController" sender:urlNote];
}
-(void) ShowNoteDeleteButtonMethod:(NSMutableArray *) btnarray
{
countNoteButtonArray = [btnarray count];
// For delete button
deleteNoteButtonWidth = 14;
deleteNoteButtonXposition = 31;
for (NSString *idNumber in btnarray)
{
deleteNoteButtonXposition = [self addDeleteButton:deleteNoteButtonXposition AndURL:idNumber];
}
}
-(int) addDeleteButton:(int) xposition AndURL:(NSString *) idNumber
{
deleteNoteButton =[ButtonClass buttonWithType:UIButtonTypeCustom];
deleteNoteButton.frame = CGRectMake(deleteNoteButtonXposition, 74.0, deleteNoteButtonWidth, 20.0);
[deleteNoteButton setImage:[UIImage imageNamed:#"delete.png"] forState:UIControlStateNormal];
[deleteNoteButton addTarget:self action:#selector(tapOnDeleteButton:) forControlEvents:UIControlEventTouchUpInside];
[deleteNoteButton setIdNum:idNumber];
deleteNoteButton.backgroundColor = [UIColor clearColor];
[self.scrollViewNote addSubview:deleteNoteButton];
deleteNoteButtonXposition = deleteNoteButtonXposition + deleteNoteButtonWidth + 65;
return deleteNoteButtonXposition;
}
-(void)tapOnDeleteButton:(ButtonClass*)sender
{
idNumb = sender.idNum;
[self.sqLite deleteData:[NSString stringWithFormat:#"DELETE FROM note WHERE id IS '%#'", idNumb]];
// NSLog(#"idNumb %#", idNumb);
//[self.view setNeedsDisplay];
//[self.view setNeedsLayout];
//[self LoadNoteButtonAndDeleteButton];
//[self viewDidLoad];
// if ([self isViewLoaded])
// {
// //self.view = Nil;
// //[self viewDidLoad];
// [self LoadNoteButtonAndDeleteButton];
// }
}
//////////////*----------------------- Note Section (Up) -----------------------*//////////////
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"goToNoteDetailsViewController"])
{
NoteDetailsViewController *noteDetailsViewController = [segue destinationViewController];
[noteDetailsViewController setUrl:sender];
}
}
Here's the screen shot:
Here we can feel the difference between UIScrollView and UICollectionView, however UICollectionView is made up of UIScrollView, UICollectionView can be reload and adjust its content accordingly, where UIScrollView can't.
Ok, now in your case, you've to reload (refresh) your scroll view, which is not possible as we can with UICollectionView or UITableView.
You've two options,
Best option (little tough) : replace UIScrollView with UICollectionView - will take some of your time, but better for reducing code complexity and good performance of your app.
Poor option (easy) : Stay as it with UIScrollView - when you want to reload, delete each subview from it, and then again show and load everything. Highly not recommended.
IMHO, you should go with best option.

How to Get Image from Animated UIImageView in iOS

So i have a UIImageView and an NSMutuableArray of four UIImages.
I just made this UIImageView animated, all the four images are animating in this imageview in a series perfectly as it should be.
Now what i want is: When user tapped on ImageView,
Which Image is just tapped by USER.
UIImageView User Intraction is enabled
UIGestureRecognizerDelegate is Added
-(void)viewDidAppear:(BOOL)animated
{
UITapGestureRecognizer *tapGesture =
[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTaps:)];
tapGesture.numberOfTapsRequired = 1;
[myImageView addGestureRecognizer:tapGesture];
[self performSelectorInBackground:#selector(showAnimatedImages) withObject:nil];
[super viewDidAppear:animated];
}
- (void) showAnimatedImages
{
myImageView.animationImages = imagesArray;
myImageView.animationDuration = 12.0f;
[myImageView startAnimating];
}
- (void)handleTaps:(UITapGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateRecognized)
{
UIImage *selectedImage = [myImageView image]; // return nil
//OR
UIImage *selectedImage = myImageView.image; // return nil
//OR
NSData *imgData = UIImagePNGRepresentation(myImageView.image); // return nil
}
}
as you see myImageView.image is always giving me a nil Value.
so please tell me how can i get image from this animating imageview.
This solution will be bit different way.
Basically from the animated imageview we can't get the current image.
So do the animation in some other way
-(void)animateImages
{
count++;
[UIView transitionWithView:imageSlideshow
duration:2.0f // this is caliculated as animationduration/numberofimage
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^{
imageSlideshow.image = [imagesArray objectAtIndex: count % [imagesArray count]];
} completion:^(BOOL finished) {
[self animateImages];
}];
}
Now in your tap gesture you will get the image
For tap gesture not working with your edited solution, do make sure that userinteraction for your imageview is enabled and you have correctly set up the gesture for the tapgesture. If it still doesnt work then :
I am not proud of this solution and yes it is not a very good solution but if you don't find anything else you can always use this.
You can do find which image is currently selected by manually calculating that.
- (void) showAnimatedImages
{
float duration = 6;
index = 0; // declared in .h file
imageView.animationImages = imagesArray;
imageView.animationDuration = duration;
[imageView startAnimating];
float secs = duration/[imagesArray count];
// timer also declared in .h file
timer = [NSTimer scheduledTimerWithTimeInterval:secs target:self selector:#selector(changeToNextImage) userInfo:nil repeats:YES];
}
-(void)handleTaps:(UITapGestureRecognizer*)sender
{
NSLog(#"current Selected Image is at index %d",index);
}
-(void)changeToNextImage
{
index++;
if(index == [imagesArray count])
index = 0;
}
Make sure to invalidate the timer when you are done with the animations.
enter code here
if (myImageView.image){
// Image on Imageview
}else {
// Image is not available on Imageview
}
The above condition always go to else block , better follow this post
http://stackoverflow.com/questions/12858028/detect-which-image-was-clicked-in-uiimageview-with-animationimages
Thanks to everyone who tried to help me on this issue.
I am posting the solution i just found from the different suggestions on my Question and from some other posts too.
in my viewDidAppear method
-(void)viewDidAppear:(BOOL)animated
{
timer = [NSTimer scheduledTimerWithTimeInterval:6.0f target:self selector:#selector(showAnimatedImages) userInfo:nil repeats:YES];
}
and the Selector Method is
- (void) showAnimatedImages
{
if (index > [imagesArray count])
{
index = 0; // start from first index again
}
myImageView.image = [imagesArray objectAtIndex:index];
//NSLog(#"View index is = %d",index);
index ++;
}
and Tap Gesture Handling method,,, as i have only four images in Array.
- (void)handleTaps:(UITapGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateRecognized)
{
if (index == 1)
{
// do related stuff
}
else if (index == 2)
{
// do related stuff
}
else if (index == 3)
{
// do related stuff
}
else if (index == 4)
{
// do related stuff
}
}
}
before moving to next view or scene i just do
[timer invalidate];
So UIImageView Animation and Image Selection on Tap Gesture is achieved ... :)

How to reset an scene in Cocos2d?

How can I reset an scene (restart the countdown and set the score to 0) the second time I start it? The second time I load the scene both initWithParameters and onStart are not called.
Cocos2D Game:
-(id) initWithParameters:(NSArray *) parameters
{
NSLog(#"init TheButton with parameters");
// Apple recommend assigning self with supers return value
self = [super init];
if (!self) return(nil);
gameParameters = parameters;
// Enable touch handling on scene node
[self setIsTouchEnabled:YES];
// Create a colored background (Dark Grey)
//CCNodeColor *background = [CCNodeColor nodeWithColor:[CCColor colorWithRed:0.6f green:0.6f blue:0.6f alpha:1.0f]];
//[self addChild:background];
CCMenuItem *starMenuItem = [CCMenuItemImage
itemFromNormalImage:#"ButtonStar.png" selectedImage:#"ButtonStarSel.png"
target:self selector:#selector(increasePoints:)];
CGSize size = [[CCDirector sharedDirector] winSize];
starMenuItem.anchorPoint = ccp(0.5f,0.5f);
starMenuItem.position = ccp(size.width/2, size.height/2);
CCMenu *starMenu = [CCMenu menuWithItems:starMenuItem, nil];
starMenu.position = CGPointZero;
[self addChild:starMenu];
timeLabel = [CCLabelTTF labelWithString:#"0.00" fontName:#"Arial" fontSize:18.0f];
//timeLabel.positionType = CCPositionTypeNormalized;
timeLabel.position = ccp(30,size.height-10);
// Add the time label
[self addChild:timeLabel];
NSLog(#"time: %#", [gameParameters objectAtIndex:0]);
clicksLabel = [CCLabelTTF labelWithString:#"0" fontName:#"Arial" fontSize:18.0f];
//clicksLabel.positionType = CCPositionTypeNormalized;
clicksLabel.position = ccp(size.width - 20,size.height-10);
// Add the time label
[self addChild:clicksLabel];
[self startGame];
return self;
}
-(void) startGame
{
clicks = 0;
myTime = [[gameParameters objectAtIndex:0] intValue];
[self schedule:#selector(update:)];
}
The scene (a cocos2d game) is integrated in a storyboard project. When it finishes (the countdown arrives to 0) it loads a new viewcontroller. From this viewcontroller I would like to load the game again when when the user press the "Play Again" Button. What happens is the scene is loading (I implemented it with an unwind segue) but both the countdown and the score are not reseted, how can I do this?
EDIT:
I finally get to reload and reset the scene from the CocosViewController but when I call performSegueWithIdentifier when the game finishes it does not load the viewcontroller. Method viewdidload is called but it does not show anything.
Cocos2dGame:
-(void) onExit
{
[super onExit];
//Send parameters
self.gameTracking = [[NSMutableArray alloc] initWithObjects:#"THE",#"QUICK", #"BROWN", #"FOX",#"FOO", nil];
NSDictionary * userInfo = [[NSMutableDictionary alloc] initWithObjectsAndKeys: [NSString stringWithFormat:#"%d",clicks], #"score", self.gameTracking, #"gameTracking", nil];
//Send a notification to the CocosViewController when the game is finished to load the new view controller
[[NSNotificationCenter defaultCenter] postNotificationName:#"GameEndedNotification" object:self userInfo:userInfo];
NSLog(#"game exit");
//[[CCDirector sharedDirector] end];
}
Cocos2DViewController
//Receive notification when the game finished
-(void)receiveNotificationFromGame:(NSNotification *) notification
{
if ([[notification name] isEqualToString:#"GameEndedNotification"])
{
NSLog (#"Successfully received the test notification! %#", [self class]);
NSLog(#"score: %d", [notification.userInfo[#"score"] intValue]);
//get variables
try.score = [notification.userInfo[#"score"] intValue];
NSLog(#"score: %d", try.score);
try.gameTracking = notification.userInfo[#"gameTracking"];
//show the next view
[self performSegueWithIdentifier:#"sg_showScore" sender:self];
}
}

show different view with different tap count from UIBarButtonItem (wrap an UISegmentedControl)

i have read about UITouch and UIGestureRecognizer, but i still really confused what the difference between them.
I have one case. In my app, i have a barbuttonitem, i want to show different view when a tap that button. if i do a singletap, i want to show a textview, but when i do a singletap again, i want to show a popover from that button. is there somebody can give me an example code to do it and give a little explanation about what is the difference between UITouch and UIGestureRecognizer???
UPDATE
the barbuttonitem is a wrapper of UISegmentedControl, here's a pic from the desain
i was try to use touchesBegan:withEvent: and touchesEnded:withEvent: to solve this problem, but i dont know how to connect it to that barbuttonitem.
This is the code i made :
-(void)addSegment{
NSAutoreleasePool *pool;
int count_doc = [_docsegmentmodels count];
NSLog(#"count doc add segment : %d", count_doc);
pool = [[NSAutoreleasePool alloc] init];
DocSegmentedModel *sl;
NSMutableArray *segmentTextMutable = [NSMutableArray array];
for(int i=0 ;(i<count_doc && i < max_segment);i++){
sl = [_docsegmentmodels objectAtIndex:i];
NSString *evalString = [[KoderAppDelegate sharedAppDelegate] setStringWithLength:sl.docSegmentFileName:10];
[segmentTextMutable addObject:NSLocalizedString(evalString,#"")];
}
NSArray *segmentText = [segmentTextMutable copy];
_docSegmentedControl = [[UISegmentedControl alloc] initWithItems:segmentText];
_docSegmentedControl.selectedSegmentIndex = 0;
_docSegmentedControl.autoresizingMask = UIViewAutoresizingFlexibleHeight;
_docSegmentedControl.segmentedControlStyle = UISegmentedControlStyleBezeled;//UISegmentedControlStylePlain;// UISegmentedControlStyleBar;//UISegmentedControlStyleBezeled;
//docSegmentedControl.frame = CGRectMake(0, 0, 800, 46.0);
[_docSegmentedControl addTarget:self action:#selector(docSegmentAction:) forControlEvents:UIControlEventValueChanged];
// Add the control to the navigation bar
//UIBarButtonItem *segmentItem = [[UIBarButtonItem alloc] initWithCustomView:_docSegmentedControl];
segmentItem = [[UIBarButtonItem alloc] initWithCustomView:_docSegmentedControl];
self.navItem.leftBarButtonItem = segmentItem;
self.navItem.leftBarButtonItem.title = #"";
[pool release];
[segmentItem release];
[_docSegmentedControl release];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
[NSObject cancelPreviousPerformRequestsWithTarget:self
selector:#selector(segmentItemTapped:) object:segmentItem];
}
-(void)touchesEnd:(NSSet *)touches withEvent:(UIEvent *)event{
if (touches.count == 1) {
if (theTouch.tapCount == 2) {
[self performSelector:#selector(segmentItemTapped:)withObject:segmentItem afterDelay:0.35];
}else {
[self performSelector:#selector(docSegmentAction:) withObject:segmentItem afterDelay:0.0];
}
}
}
- (IBAction)segmentItemTapped:(id)sender{
if (self.fileProperties == nil) {
self.fileProperties = [[[FilePropertiesViewController alloc] initWithNibName:#"FilePropertiesViewController" bundle:nil]autorelease];
fileProperties.delegate = self;
self.filePropertiesPopover = [[[UIPopoverController alloc] initWithContentViewController:fileProperties]autorelease];
[_docsegmentmodels objectAtIndex:_docSegmentedControl.selectedSegmentIndex];
}
fileProperties.docProperties = _docsegmentmodels;
fileProperties.index = _docSegmentedControl.selectedSegmentIndex; //index utk nilai2 di textfield
[self.filePropertiesPopover presentPopoverFromBarButtonItem:sender
permittedArrowDirections:UIPopoverArrowDirectionUp
animated:YES];
}
- (IBAction)docSegmentAction:(id)sender{
NSLog(#"open file");
isFileOpen = YES;
[self.moreFilePopOverController dismissPopoverAnimated:YES];
[self.filePropertiesPopover dismissPopoverAnimated:YES];
[self showTextView];
}
is there any mistake in my understanding?
In your case you don't need UIGestureRecognizer. Simply set the action of the barbuttonitem to the method that should be called when the button is tapped. Have the method keep track of the state of the textview and depending on it show it or show the popover.

Resources