UIWebView addSubview:UIImageView not displaying when triggered by touch events - objective-c

A new day, a new question!
I have been working on an app that requires I show a circle where the users finger(s) is(are) when they are touching the screen.
I have followed a tutorial I found on subclassing UIWindow, and my comment explains how to pass on those touches if you are using Storyboards and ARC.
I am using the following code (which is based on this tutorial) to make a touch indicator to display:
#pragma mark - Touch Events
- (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
NSArray *subviews = [self.webView subviews];
for (UIView *view in subviews)
{
// Check if view is scrollview
if ([view isKindOfClass:[UserTouchImageView class]])
{
[view removeFromSuperview];
}
}
// Enumerate over all the touches and draw a red dot on the screen where the touches were
[touches enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
// Get a single touch and it's location
UITouch *touch = obj;
CGPoint touchPoint = [touch locationInView:self.webView];
// Add touch indicator
UserTouchImageView *touchView = [[UserTouchImageView alloc] initWithFrame:CGRectMake(touchPoint.x -15, touchPoint.y -15, 30, 30)];
[self.webView addSubview:touchView];
}];
NSLog(#"Touches began");
}
- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
NSArray *subviews = [self.webView subviews];
for (UIView *view in subviews)
{
// Check if view is scrollview
if ([view isKindOfClass:[UserTouchImageView class]])
{
[view removeFromSuperview];
}
}
// Enumerate over all the touches and draw a red dot on the screen where the touches were
[touches enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
// Get a single touch and it's location
UITouch *touch = obj;
CGPoint touchPoint = [touch locationInView:self.webView];
// Draw a red circle where the touch occurred
UserTouchImageView *touchView = [[UserTouchImageView alloc] initWithFrame:CGRectMake(touchPoint.x -15, touchPoint.y -15, 30, 30)];
[self.webView addSubview:touchView];
}];
NSLog(#"Touches moved");
}
- (void) touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
NSArray *subviews = [self.webView subviews];
for (UIView *view in subviews)
{
// Check if view is scrollview
if ([view isKindOfClass:[UserTouchImageView class]])
{
[UIView animateWithDuration:0.5f
animations:^{
view.alpha = 0.0;
}
completion:^(BOOL finished) {
[view removeFromSuperview];
}];
}
}
NSLog(#"Touches ended");
}
- (void) touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event {
NSArray *subviews = [self.webView subviews];
for (UIView *view in subviews)
{
// Check if view is scrollview
if ([view isKindOfClass:[UserTouchImageView class]])
{
[view removeFromSuperview];
}
}
NSLog(#"Touches cancelled");
}
UserTouchImageView is a subclass of UIImageView, with the change to the default being:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
self.image = [UIImage imageNamed:#"greyCircle.png"];
self.alpha = 0.5f;
}
return self;
}
I've tested this code without a UIWebview (replacing the self.webView with self.view), and it works fine, drawing the circle where I click and drag, then fading out when I release the button.
I have also succeeded in instantiating the UserTouchImageView and adding it to the webView from the viewDidLoad method of the viewController.
As soon as I put the UIWebview in with the code above, the same line ([self.webView addSubview:touchView];) doesn't work. I still get the NSLog messages to the console, but the subview doesn't get visibly added.
Can anyone help me figure out why this doesn't appear? Is there any other code I need to be aware of, or any reason why I cannot do this?
Many thanks!
EDIT
I have uploaded my source so far here (MediaFire)
EDIT 2
I've added two methods as below:
-(void)listWebViewSubViews
{
NSLog(#"BEGIN LISTING SUBVIEWS");
for (UIView *view in [self.webView subviews]) {
NSLog(#"Subview: %#", view.description);
}
NSLog(#"END LISTING SUBVIEWS");
}
-(void)view:(UIView *)view addTouchIndicatorWithCentreAtPoint:(CGPoint)point
{
NSLog(#"View: %#, x: %f, y: %f", view.description, point.x, point.y);
// Add touch indicator
UserTouchImageView *touchView = [[UserTouchImageView alloc] initWithFrame:CGRectMake(point.x -15, point.y -15, 30, 30)];
[view addSubview:touchView];
}
These are called from two buttons outside the UIWebView, and from within the touchesbegan method:
- (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
NSArray *subviews = [self.webView subviews];
for (UIView *view in subviews)
{
if ([view isKindOfClass:[UserTouchImageView class]])
{
[view removeFromSuperview];
}
}
[touches enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
UITouch *touch = obj;
CGPoint touchPoint = [touch locationInView:self.webView];
[self view:self.webView addTouchIndicatorWithCentreAtPoint:touchPoint];
[self listWebViewSubViews];
}];
NSLog(#"Touches began");
}
The logging seems to indicate that the webview referenced by self.webView WITHIN the touches began method is an entirely seperate instance than the one when referenced from the two buttons. I can add multiple subviews via the button, and then list them, and they all show up in the logging, yet when I click to touch on the simulator, none of these extra subviews get listed.
Does anyone know of any special functionality in a UIWebView to have a duplicate instance on touch events?

A UIWebView is not one view but a collection of views. When you add a subview to a UIWebView the new subview is added to a hierarchy of subviews. Your new subview is probably being added behind other opaque views. It's there, but hidden. I'd recommend adding this indicator view to a different view in the hierarchy.

The default value of userInteractionEnabled for UIImageView is NO. You may need to set it to YES.

Problem solved:
Unfortunately this was caused by somethign entirely different, so thank you everyone for the help you've given.
The problem for this arose from my attempt to fix the code to run with storyboards. I had tried to access the view controller from the storyboard, and add that as the view to receive touch events as a priority (see the tutorial I linked to, and my comment, which I have also responded to). Where as I should have been using self.window.rootViewController.
My updated comment is here

Related

pan gesture recognise button issue

I set a pan gesture recogniser to recognise my touch on some buttons and am running into the following issue. I am trying to add an action to each of the buttons. I'm testing this by telling each different button to become highlighted when I touch them. So far when I keep my finger pressed on the screen en slide around only button1 and button2 show up (as written in the code).
But for some reason I can still see other buttons highlight the same way when I press them individually. Any idea how to solve this so that they only respond to. If button.tag == 3 etc.. Then respond? Here is the code. (This is all the code in the project and a few buttons in the interface builder.)
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// Add Gesture to track the finger
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePanGesture:)];
[self.view addGestureRecognizer:pan];
}
- (void)handlePanGesture:(UIPanGestureRecognizer *)gesture
{
if (gesture.state == UIGestureRecognizerStateChanged) {
CGPoint point = [gesture locationInView:self.view];
for (UIButton *button in [self.view subviews]) {
if ([button isKindOfClass:[UIButton class]]) {
if (button.tag == 1) {
button.highlighted = CGRectContainsPoint(button.frame, point);
} else if (button.tag == 2) {
button.highlighted = CGRectContainsPoint(button.frame, point);
} //
}
}
}
else if (gesture.state == UIGestureRecognizerStateEnded)
{
for (UIButton *button in [self.view subviews]) {
if ([button isKindOfClass:[UIButton class]]) {
button.highlighted = NO;
}
}
}
}
EDIT :
[[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePanned:)];
- (void)handlePanned:(UIPanGestureRecognizer*)thePanner{
if (thePanner.state == UIGestureRecognizerStateChanged ){
//disable button
}else if (thePanner.state == UIGestureRecognizerStateEnded) {
//enable button
}else if ( thePanner.state == UIGestureRecognizerStateFailed ){
//enable button
}
}
You aren't checking to see if where you are touching is where a button is located.
Quickest solution I see is:
Create properties for each of your buttons, we'll call them button1, button2 and button3.
Create your panGestureRecognizer
- (void)viewDidLoad
{
[super viewDidLoad];
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePanGesture:)];
[self.view addGestureRecognizer:pan];
}
Your method to handle the pan:
-(void)handlePanGesture:(UIPanGestureRecognizer *)gesture
{
//create a CGpoint so you know where you are touching
CGPoint touchPoint = [gesture locationInView:self.view];
//just to show you where you are touching...
NSLog(#"%#", NSStringFromCGPoint(touchPoint));
//check your button frame's individually to see if you are touching inside it
if (CGRectContainsPoint(self.button1.frame, touchPoint))
{
NSLog(#"you're panning button1");
}
else if(CGRectContainsPoint(self.button2.frame, touchPoint))
{
NSLog(#"you're panning button2");
}
else if (CGRectContainsPoint(self.button3.frame, touchPoint))
{
NSLog(#"you're panning button3");
}
And that should be it.

UIWebView display PDF and hide gray shadow in iOS7

I'm trying to remove/hide the shadow from an UIWebView that displays a PDF in iOS7.
I've tried all solutions on stackoverflow and also others from the Internet, but it doesn't work.
Maybe it's because I'm using NSURLSession to load a PDF from a server and then display it.
Right now it looks like this:
My first guess was that it doesn't work because the NSURLSession delegates are not on the main thread but even if I remove the subviews (that contain the shadow) on the main thread and call setNeedsDisplay it doesn't change.
I'm starting a DownloadTask and when the task is finished and the delegate gets called I remove the layers.
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location {
NSData *data = [NSData dataWithContentsOfURL:location];
[self.webView loadData:data MIMEType:#"application/pdf" textEncodingName:#"utf-8" baseURL:nil];
// remove shadow layers from scrollview
dispatch_async(dispatch_get_main_queue(), ^{
self.webView.scalesPageToFit = YES;
for (UIView* subView in [self.webView subviews])
{
if ([subView isKindOfClass:[UIScrollView class]]) {
for (UIView* shadowView in [subView subviews])
{
if ([shadowView isKindOfClass:[UIImageView class]]) {
[shadowView setHidden:YES];
}
}
}
}
[self.webView.layer setNeedsDisplay];
});
}
Even if a remove the GCD async block and it's executed in the same thread it doesn't change anything. I've also tried to call it in viewDidLoad and viewDidAppear.
Any tips are highly appreciated!
- (void)webViewDidFinishLoad:(UIWebView *)webView {
for (UIView *object in webView.scrollView.subviews) {
if ([NSStringFromClass([object class]) isEqualToString:#"UIWebPDFView"]) {
UIView *pdfView = object;
for (UIView *pdfObjectSubview in pdfView.subviews) {
if ([NSStringFromClass([pdfObjectSubview class]) isEqualToString:#"UIPDFPageView"]) {
UIView *uiPDFPageView = pdfObjectSubview;
uiPDFPageView.layer.shadowOpacity = 0.0f;
}
}
}
}
}

Xcode keyboard issue on scroll view

I have a scroll view on top single view. I have some textfields and UIPickers on it. Now I know how to make a keyboard go off when return is pushed. But, I am trying to get the keyboard off from textfield when the background is tapped or UIpicker is selected. I tried doing this...
Interface :
- (IBAction)textFieldReturn:(id)sender;
- (IBAction)backgroundTouched:(id)sender;
Implementation :
-(IBAction)textFieldReturn:(id)sender
{
[sender resignFirstResponder];
}
-(IBAction)backgroundTouched:(id)sender
{
[textField resignFirstResponder];
}
But the problem is I cant make sroll view as control type to make it work..
Try like this may be it helps you but not sure,
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [[event allTouches] anyObject];
if (![[touch view] isKindOfClass:[UITextField class]]) {
[yourtextfield resignFirstResponder];
}
}
And for getting touch event on scrollview you have to take geasture recognization,
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(singleTap)];
[scroll addGestureRecognizer:singleTap];
-(void)singleTap{
[text resignFirstResponder];
//write whatever you want it.
}

Can I add custom UI Elements to UIActionSheet?

I've successfully added Buttons to my UIActionSheet. I was wondering if I could add other elements, adjust the opacity, and alter the direction of the panel flying in (default comes in from below)with my UIActionSheet?
I am relatively new to iOS programming so any help would be greatly appreciated.
If you’re going to customize it to that degree, you’re better off just creating your own custom overlay view and adding whatever controls you need to it. Adjusting the animation direction in particular would be more trouble than it’s worth.
For more information on custom views and the view hierarchy, plus a useful section on animations (see the sidebar), check out the View Programming Guide for iOS.
Write implemention!
//.h
#import <UIKit/UIKit.h>
#interface UIImageActionSheet : UIActionSheet {
UIImage *titleImage;
}
-(id) initWithImage:(UIImage *)image
title:(NSString *)title
delegate:(id <UIActionSheetDelegate>)delegate
cancelButtonTitle:(NSString *)cancelButtonTitle
destructiveButtonTitle:(NSString *)destructiveButtonTitle
otherButtonTitles:(NSString *)otherButtonTitles;
#end
//.m file
#import "UIImageActionSheet.h"
#implementation UIImageActionSheet
-(id) initWithImage:(UIImage *)image
title:(NSString *)title
delegate:(id <UIActionSheetDelegate>)delegate
cancelButtonTitle:(NSString *)cancelButtonTitle
destructiveButtonTitle:(NSString *)destructiveButtonTitle
otherButtonTitles:(NSString *)otherButtonTitles{
self = [super initWithTitle:title delegate:delegate
cancelButtonTitle:cancelButtonTitle
destructiveButtonTitle:destructiveButtonTitle
otherButtonTitles:otherButtonTitles,nil];
if (self) {
titleImage=image;
[titleImage retain];
UIImageView *imageView = [[UIImageView alloc] initWithImage:titleImage];
imageView.frame = CGRectZero;
for (UIView *subView in self.subviews){
if (![subView isKindOfClass:[UILabel class]]) {
[self insertSubview:imageView aboveSubview:subView];
break;
}
}
[imageView release];
}
return self;
}
- (CGFloat) maxLabelYCoordinate {
// Determine maximum y-coordinate of labels
CGFloat maxY = 0;
for( UIView *view in self.subviews ){
if([view isKindOfClass:[UILabel class]]) {
CGRect viewFrame = [view frame];
CGFloat lowerY = viewFrame.origin.y + viewFrame.size.height;
if(lowerY > maxY)
maxY = lowerY;
}
}
return maxY;
}
-(void) layoutSubviews{
[super layoutSubviews];
CGRect frame = [self frame];
CGFloat labelMaxY = [self maxLabelYCoordinate];
for(UIView *view in self.subviews){
if (![view isKindOfClass:[UILabel class]]) {
if([view isKindOfClass:[UIImageView class]]){
CGRect viewFrame = CGRectMake((320 - titleImage.size.width)/2, labelMaxY + 10,
titleImage.size.width, titleImage.size.height);
[view setFrame:viewFrame];
}
else if(![view isKindOfClass:[UIImageView class]]) {
CGRect viewFrame = [view frame];
viewFrame.origin.y += titleImage.size.height+10;
[view setFrame:viewFrame];
}
}
}
frame.origin.y -= titleImage.size.height + 2.0;
frame.size.height += titleImage.size.height + 2.0;
[self setFrame:frame];
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code.
}
*/
- (void)dealloc {
[super dealloc];
if (titleImage) {
[titleImage release];
}
}
#end
You have to create a custom overlay

Removing and reinserting UIView into UIViewController

I have a UIViewController that contains a UIView.
Between every time the viewcontroller is displyaed, the UIView has to be cleared and the content reloaded.
The problem is that the old content still appears in the UIView.
Load data before controller becomes visible:
- (void)viewWillAppear:(BOOL)animated
{
contentView = [[ContentView alloc] initWithFrame:CGRectMake(10, 10, 100, 100)];
contentView.userInteractionEnabled = NO;
[self.view addSubview:contentView];
if([self loadContentData] == NO) {
[contentView release];
return NO;
}
return YES;
}
Remove content after controller is hidden:
- (void)viewDidDisappear:(BOOL)animated
{
[contentView removeFromSuperview];
[contentView release];
}
Why is this cleanup not sufficient?
Try:
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[contentView removeFromSuperView]; //releases contentView
}
You must call super's method when overriding this method.
Additionally, contentView appears to be over-released in the code you've posted which makes me believe you are retaining it possibly somewhere in your real code. If this is the case, you may have over-retained contentView, which will prevent it from being released and cleaned up from your view hierarchy.
I would suggest you explore something along these lines:
- (void)viewWillAppear:(BOOL)animated
{
contentView = [[[ContentView alloc] initWithFrame:CGRectMake(10, 10, 100, 100)] autorelease];
contentView.userInteractionEnabled = NO;
[self.view addSubview:contentView];
if([self loadContentData] == NO) {
//[contentView release]; //ContentView is now autoreleased and will be dropped when the method exits.
return NO; //this probably has a compiler warning, since it is a (void) method
}
return YES; //this probably has a compiler warning, since it is a (void) method
}