UIView (subclass) trying to present UIImagePickerController - ios

I have a parent view that allows you to see post in a UITableView. In its Navigation Bar I have a post button that when pressed presents a UIView subclass and shows it on the top of the screen. I have an image on that UIView that when tapped I want to present the UIImagePickerController to allow users to pick an image to post to the service. How can I do this since my subview is not a view controller it cannot present the UIImagePickerController.
Below is my subview code.
#import "PostView.h"
#implementation PostView
#synthesize attachedLabel;
#synthesize postButton;
#synthesize textView;
#synthesize characterLimit;
#synthesize attachImage;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
originalFrame = frame;
NSArray *xib = [[NSBundle mainBundle] loadNibNamed:#"PostView" owner:self options:nil];
PostView *view = [xib objectAtIndex:0];
[view setBackgroundColor:[UIColor whiteColor]];
[view setAlpha:0.7f];
attachedLabel = [[UILabel alloc] initWithFrame:CGRectMake(204, 212, 56, 21)];
attachedLabel.textColor = [UIColor blackColor];
[attachedLabel setText:#"Attached"];
attachedLabel.backgroundColor = [UIColor clearColor];
attachedLabel.font = [UIFont fontWithName:text_font_name size:12.0];
characterLimit = [[UILabel alloc] initWithFrame:CGRectMake(246, 13, 50, 21)];
[characterLimit setTextAlignment:NSTextAlignmentRight];
characterLimit.textColor = [UIColor blackColor];
characterLimit.backgroundColor = [UIColor clearColor];
characterLimit.font = [UIFont fontWithName:text_font_name size:12.0];
attachImage = [[UIImageView alloc] initWithFrame:CGRectMake(270, 208, 30, 30)];
[attachImage setImage:[UIImage imageNamed:#"attachphoto30x30.png"]];
[self.textView setDelegate:self];
[self.textView setAlpha:0.7f];
[self.textView setTextColor:[UIColor whiteColor]];
[self.textView setBackgroundColor:[UIColor clearColor]];
self.layer.cornerRadius = 10.0f;
self.layer.masksToBounds = YES;
[self addSubview:view];
[self addSubview:characterLimit];
[self addSubview:attachedLabel];
[self addSubview:attachImage];
}
return self;
}
- (IBAction)openCamera:(id)sender
{
UIImagePickerController *controller = [[UIImagePickerController alloc] init];
controller.delegate = self;
//[self presentViewController:controller animated:YES completion:nil];
NSLog(#"%#", #"Image Tapped");
}
-(void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary*)info
{
/*[picker dismissViewControllerAnimated:YES completion:nil];
UIImage *image = [info objectForKey: UIImagePickerControllerOriginalImage];
UIImage *scale = [image scaleToSize:CGSizeMake(320.0f, 548.0f)];
imageData = UIImageJPEGRepresentation(scale, 1);
encodedImage = [self Base64Encode:imageData];
[attachedLabel setHidden:NO];
*/
}
#pragma mark Custom alert methods
- (IBAction)postAction:(id)sender
{
[self hide];
}
- (void)show
{
//prepare attachImage
attachImage.userInteractionEnabled = YES;
UITapGestureRecognizer *tapAttach = [[UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(openCamera:)];
tapAttach.numberOfTapsRequired = 1;
[self.attachImage addGestureRecognizer:tapAttach];
isShown = YES;
self.transform = CGAffineTransformMakeScale(0.1, 0.1);
self.alpha = 0;
[UIView beginAnimations:#"showAlert" context:nil];
[self setBackgroundColor:[UIColor clearColor]];
[UIView setAnimationDelegate:self];
self.transform = CGAffineTransformMakeScale(1.1, 1.1);
self.alpha = 1;
[UIView commitAnimations];
}
- (void)hide
{
isShown = NO;
[UIView beginAnimations:#"hideAlert" context:nil];
[UIView setAnimationDelegate:self];
[[NSNotificationCenter defaultCenter] postNotificationName:#"hidePostView_Notification" object:nil];
self.transform = CGAffineTransformMakeScale(0.1, 0.1);
self.alpha = 0;
[UIView commitAnimations];
}
- (void)toggle
{
if (isShown)
{
[self hide];
} else
{
[self show];
}
}
#pragma mark Animation delegate
- (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context
{
if ([animationID isEqualToString:#"showAlert"])
{
if (finished)
{
[UIView beginAnimations:nil context:nil];
self.transform = CGAffineTransformMakeScale(1.0, 1.0);
[UIView commitAnimations];
}
} else if ([animationID isEqualToString:#"hideAlert"])
{
if (finished)
{
self.transform = CGAffineTransformMakeScale(1.0, 1.0);
self.frame = originalFrame;
}
}
}
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView
{
return YES;
}
- (BOOL)textView:(UITextView *)textViewer shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)string
{
if ([string isEqualToString:#"\n"])
{
[textViewer resignFirstResponder];
}
return [self isAcceptableTextLength:textViewer.text.length + string.length - range.length];
}
-(IBAction)checkIfCorrectLength:(id)sender
{
if (![self isAcceptableTextLength:self.textView.text.length])
{
// do something to make text shorter
}
}
- (BOOL)isAcceptableTextLength:(NSUInteger)length
{
return length <= 160;
}
- (void)textViewDidChange:(UITextView *)textViewer
{
NSString *characters = [[NSString stringWithFormat:#"%d", textViewer.text.length] stringByAppendingString:#"/160"];
NSLog(#"%#", characters);
[self updateDisplay:characters];
}
-(void) updateDisplay : (NSString *)str
{
[self.characterLimit performSelectorOnMainThread : # selector(setText : ) withObject:str waitUntilDone:YES];
}
#end

Yes, you can not present a viewcontroller from a UIView subclass.
To solve this problem, you can use your subview's superview's viewcontroller class. calling [self.superview nextResponder] in your subview will return you the superview's viewcontroller. Using that you can present your UIImagePicker view controller. To use the presentViewController method, you should cast [self.superview nextResponder] to your parentviewcontroller's class type. Also make sure you import parentview controller.h inside subview.m file
[(YourParentViewController *)[self.superview nextResponder] presentViewController:controller animated:YES completion:nil];

You should present a UIViewController subclass rather than a UIView subclass.
I would also say that UIViewController should be responsible for handling data and operational logic for its views. Check out some of the docs:
View Controller Basics:
http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/AboutViewControllers/AboutViewControllers.html
UIViewController Class Reference:
http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/AboutViewControllers/AboutViewControllers.html

Related

How to create custom slide menu without third party library.?

I want to implement slide menu in my iOS app like drawer (Andriod). I went through a tutorial, but all of them are using third party libraries. Is there any possibility to create a custom slide menu. I tried to create it with the following code, but it's only working with xib file:
- (IBAction)sidemenu:(id)sender
{
[UIView animateWithDuration:0.50f animations:^{
view.frame = self.view.frame;
} completion:^(BOOL finished) {
swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(SwipGestureLeftAction:)];
swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
[self.view addGestureRecognizer:swipeLeft];
}];
}
- (void)SwipGestureAction
{
UISwipeGestureRecognizer *swiperight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(SwipGestureRightAction:)];
swiperight.direction = UISwipeGestureRecognizerDirectionRight;
[self.view addGestureRecognizer:swiperight];
}
#pragma mark AddSwipeGestureLeftAndRight
- (void)SwipGestureRightAction:(UISwipeGestureRecognizer *)swipeRight
{
[UIView animateWithDuration:0.50f animations:^{
view.frame = self.view.frame;
} completion:^(BOOL finished) {
swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(SwipGestureLeftAction:)];
swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
[self.view addGestureRecognizer:swipeLeft];
}];
}
- (void)SwipGestureLeftAction:(UISwipeGestureRecognizer *)swipeRight
{
[UIView animateWithDuration:0.50f animations:^{
[view setFrame:CGRectMake(self.view.frame.origin.x - self.view.frame.size.width, self.view.frame.origin.y, self.view.frame.size.width, self.view.frame.size.height)];
} completion:^(BOOL finished){
[self.view removeGestureRecognizer:swipeLeft];
}];
}
Here is what I have for you:
I made a super class for all my slide menus in all projects. It manages the showing and hiding of the slide menu, and handles orientation changes. It slides in from left on top of the current view, and it partially obscures the remainder of the view with a dark transparent background.
If you ever need other behaviour (like pushing out the current view) just override the animation part.
My slide menu is a Singleton because in our applications we only use one slide menu on every screen.
#import <UIKit/UIKit.h>
#interface IS_SlideMenu_View : UIView <UIGestureRecognizerDelegate>
{
UIView* transparentBgView;
BOOL hidden;
int lastOrientation;
}
#property (strong, nonatomic) UIView *menuContainerV;
+ (id)sharedInstance;
- (BOOL)isShown;
- (void)hideSlideMenu;
- (void)showSlideMenu;
#end
#import "IS_SlideMenu_View.h"
#implementation IS_SlideMenu_View
+ (id)sharedInstance
{
static id _sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedInstance = [[[self class] alloc] init];
});
return _sharedInstance;
}
- (instancetype)initWithFrame:(CGRect)frame
{
frame = [[[UIApplication sharedApplication] delegate] window].frame;
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor clearColor];
transparentBgView = [[UIView alloc] initWithFrame:frame];
[transparentBgView setBackgroundColor:[UIColor colorWithRed:0 green:0 blue:0 alpha:0.6]];
[transparentBgView setAlpha:0];
transparentBgView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(gestureRecognized:)];
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(gestureRecognized:)];
[transparentBgView addGestureRecognizer:tap];
[transparentBgView addGestureRecognizer:pan];
[self addSubview:transparentBgView];
frame.size.width = 280;
self.menuContainerV = [[UIView alloc] initWithFrame:frame];
CALayer *l = self.menuContainerV.layer;
l.shadowColor = [UIColor blackColor].CGColor;
l.shadowOffset = CGSizeMake(10, 0);
l.shadowOpacity = 1;
l.masksToBounds = NO;
l.shadowRadius = 10;
self.menuContainerV.autoresizingMask = UIViewAutoresizingFlexibleHeight;
[self addSubview: self.menuContainerV];
hidden = YES;
}
//----- SETUP DEVICE ORIENTATION CHANGE NOTIFICATION -----
UIDevice *device = [UIDevice currentDevice];
[device beginGeneratingDeviceOrientationNotifications];
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self selector:#selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:device];
lastOrientation = [[UIDevice currentDevice] orientation];
return self;
}
//********** ORIENTATION CHANGED **********
- (void)orientationChanged:(NSNotification *)note
{
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if(orientation == UIDeviceOrientationPortrait || orientation == UIDeviceOrientationLandscapeLeft || orientation == UIDeviceOrientationLandscapeRight){
NSLog(#"%ld",orientation);
if(!hidden && lastOrientation != orientation){
[self hideSlideMenu];
hidden = YES;
lastOrientation = orientation;
}
}
}
- (void)showSlideMenu {
UIWindow* window = [[[UIApplication sharedApplication] delegate] window];
self.frame = CGRectMake(0, 0, window.frame.size.width, window.frame.size.height);
[self.menuContainerV setTransform:CGAffineTransformMakeTranslation(-window.frame.size.width, 0)];
[window addSubview:self];
// [[UIApplication sharedApplication] setStatusBarHidden:YES];
[UIView animateWithDuration:0.5 animations:^{
[self.menuContainerV setTransform:CGAffineTransformIdentity];
[transparentBgView setAlpha:1];
} completion:^(BOOL finished) {
NSLog(#"Show complete!");
hidden = NO;
}];
}
- (void)gestureRecognized:(UIGestureRecognizer *)recognizer
{
if ([recognizer isKindOfClass:[UITapGestureRecognizer class]]) {
[self hideSlideMenu];
} else if ([recognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
static CGFloat startX;
if (recognizer.state == UIGestureRecognizerStateBegan) {
startX = [recognizer locationInView:self.window].x;
} else
if (recognizer.state == UIGestureRecognizerStateChanged) {
CGFloat touchLocX = [recognizer locationInView:self.window].x;
if (touchLocX < startX) {
[self.menuContainerV setTransform:CGAffineTransformMakeTranslation(touchLocX - startX, 0)];
}
} else
if (recognizer.state == UIGestureRecognizerStateEnded) {
[self hideSlideMenu];
}
}
}
- (void)hideSlideMenu
{
UIWindow* window = [[[UIApplication sharedApplication] delegate] window];
window.backgroundColor = [UIColor clearColor];
[UIView animateWithDuration:0.5 animations:^{
[self.menuContainerV setTransform:CGAffineTransformMakeTranslation(-self.window.frame.size.width, 0)];
[transparentBgView setAlpha:0];
} completion:^(BOOL finished) {
[self removeFromSuperview];
[self.menuContainerV setTransform:CGAffineTransformIdentity];
// [[UIApplication sharedApplication] setStatusBarHidden:NO];
hidden = YES;
NSLog(#"Hide complete!");
}];
}
- (BOOL)isShown
{
return !hidden;
}
#end
Subclasses only need to add subviews to the menuContainerV view, and manage them.
An example:
I created a subclass that has an header view and a table view as its content. I created the content view in a xib, and the owner of the xib is this subclass. This way I can bind outlets to the xib.
#import "IS_SlideMenu_View.h"
#interface CC_SlideMenu_View : IS_SlideMenu_View<UITableViewDelegate, UITableViewDataSource>
#property (weak, nonatomic) IBOutlet UIView *headerView;
#property (weak, nonatomic) IBOutlet UITableView *tableView;
...
#end
When the slide menu gets instantiated I load the xib and add the content view to the menuContainerV view.
#import "CC_SlideMenu_View.h"
#implementation CC_SlideMenu_View
- (instancetype)init
{
self = [super init];
if (self) {
UIView *v = [[[NSBundle mainBundle] loadNibNamed:#"CC_SlideMenu_View" owner:self options:nil] firstObject];
v.frame = self.menuContainerV.bounds;
[self.menuContainerV addSubview:v];
self.tableView.backgroundColor = [UIColor darkGrayColor];
}
return self;
}
...
#end
The result is something like this.

How to dismis Viewcontroller (popup) by pressing button inside my popup viewcontroller and also touch any where outside of viewcontroller?

I have one viewcontroler with one button.When i press that one pop up viewcontroller will show up.And when i touch anywhere outside it dismiss the viewcontroller .And return to my main viewcontroller.
Note
I used my popup through one viewcontroller and i use identifier name .Then i show pop up using that storyboard identifier and declared in my viewcontroller.m in btnSelectDatePressed
Needed
in that pop up view controller i have one button when user press also it should dismiss the viewcontroller and should return to my main viewcontroller.My example image is like this sample image
This is my viewcontroller.m file
#import "ViewController.h"
#import "UIViewController+ENPopUp.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (IBAction)btnSelectDatePressed:(id)sender
{
UIViewController *vc = [[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"PopUp"];
vc.view.frame = CGRectMake(0, 0, 309.0f, 531.0f);
[self presentPopUpViewController:vc];
}
This is my popup method declared in #import "UIViewController+ENPopUp.h"
#import <UIKit/UIKit.h>
#interface UIViewController (ENPopUp)
#property (nonatomic, retain) UIViewController *en_popupViewController;
- (void)presentPopUpViewController:(UIViewController *)popupViewController;
- (void)presentPopUpViewController:(UIViewController *)popupViewController completion:(void (^)(void))completionBlock;
- (void)dismissPopUpViewController;
- (void)dismissPopUpViewControllerWithcompletion:(void (^)(void))completionBlock;
//- (IBAction)disMe:(id)sender;
#end
This is my popup method declared in `#import "UIViewController+ENPopUp.m"`
#import "UIViewController+ENPopUp.h"
#import "JWBlurView.h"
#import <objc/runtime.h>
static void * ENPopupViewControllerPropertyKey = &ENPopupViewControllerPropertyKey;
static CGFloat const kAnimationDuration = .4f;
static CGFloat const kRotationAngle = 70.f;
static NSInteger const kENPopUpOverlayViewTag = 351301;
static NSInteger const kENPopUpViewTag = 351302;
static NSInteger const kENPopUpBluredViewTag = 351303;
#implementation UIViewController (ENPopUp)
#pragma mark - Public Methods
- (void)presentPopUpViewController:(UIViewController *)popupViewController
{
[self presentPopUpViewController:popupViewController completion:nil];
}
- (void)presentPopUpViewController:(UIViewController *)popupViewController completion:(void (^)(void))completionBlock
{
self.en_popupViewController = popupViewController;
[self presentPopUpView:popupViewController.view completion:completionBlock];
}
- (void)dismissPopUpViewController
{
[self dismissPopUpViewControllerWithcompletion:nil];
}
- (void)dismissPopUpViewControllerWithcompletion:(void (^)(void))completionBlock
{
UIView *sourceView = [self topView];
JWBlurView *blurView = (JWBlurView *)[sourceView viewWithTag:kENPopUpBluredViewTag];
UIView *popupView = [sourceView viewWithTag:kENPopUpViewTag];
UIView *overlayView = [sourceView viewWithTag:kENPopUpOverlayViewTag];
[self performDismissAnimationInSourceView:sourceView withBlurView:blurView popupView:popupView overlayView:overlayView completion:completionBlock];
}
//- (IBAction)disMe:(id)sender {
// UIViewController *vc = [[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"Pop"];
// [self dismissViewControllerAnimated:YES completion:Nil];
//}
#pragma mark - Getters & Setters
- (UIViewController *)en_popupViewController
{
return objc_getAssociatedObject(self, ENPopupViewControllerPropertyKey);
}
- (void)setEn_popupViewController:(UIViewController *)en_popupViewController
{
objc_setAssociatedObject(self, ENPopupViewControllerPropertyKey, en_popupViewController, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
#pragma mark - View Handling
- (void)presentPopUpView:(UIView *)popUpView completion:(void (^)(void))completionBlock
{
UIView *sourceView = [self topView];
// Check if source view controller is not in destination
if ([sourceView.subviews containsObject:popUpView]) return;
// Add overlay
UIView *overlayView = [[UIView alloc] initWithFrame:sourceView.bounds];
overlayView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
overlayView.tag = kENPopUpOverlayViewTag;
overlayView.backgroundColor = [UIColor clearColor];
// Add Blured View
JWBlurView *bluredView = [[JWBlurView alloc] initWithFrame:overlayView.bounds];
bluredView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
bluredView.tag = kENPopUpBluredViewTag;
[bluredView setBlurAlpha:.0f];
[bluredView setAlpha:.0f];
[bluredView setBlurColor:[UIColor clearColor]];
bluredView.backgroundColor = [UIColor clearColor];
[overlayView addSubview:bluredView];
// Make the background clickable
UIButton * dismissButton = [UIButton buttonWithType:UIButtonTypeCustom];
dismissButton.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
dismissButton.backgroundColor = [UIColor clearColor];
dismissButton.frame = sourceView.bounds;
[overlayView addSubview:dismissButton];
[dismissButton addTarget:self action:#selector(dismissPopUpViewController)
forControlEvents:UIControlEventTouchUpInside];
// Customize popUpView
popUpView.layer.cornerRadius = 3.5f;
popUpView.layer.masksToBounds = YES;
popUpView.layer.zPosition = 99;
popUpView.tag = kENPopUpViewTag;
popUpView.center = overlayView.center;
[popUpView setNeedsLayout];
[popUpView setNeedsDisplay];
[overlayView addSubview:popUpView];
[sourceView addSubview:overlayView];
[self setAnimationStateFrom:popUpView];
[self performAppearAnimationWithBlurView:bluredView popupView:popUpView completion:completionBlock];
}
#pragma mark - Animation
- (void)setAnimationStateFrom:(UIView *)view
{
CALayer *layer = view.layer;
layer.transform = [self transform3d];
}
- (CATransform3D)transform3d
{
CATransform3D transform = CATransform3DIdentity;
transform = CATransform3DTranslate(transform, 0, 200.f, 0);
transform.m34 = 1.0/800.0;
transform = CATransform3DRotate(transform, kRotationAngle*M_PI/180.f, 1.f, .0f, .0f);
CATransform3D scale = CATransform3DMakeScale(.7f, .7f, .7f);
return CATransform3DConcat(transform, scale);
}
- (void)performAppearAnimationWithBlurView:(JWBlurView *)blurView popupView:(UIView *)popupView completion:(void (^)(void))completionBlock
{
CATransform3D transform;
transform = CATransform3DIdentity;
[UIView animateWithDuration:kAnimationDuration
animations:^ {
[self.en_popupViewController viewWillAppear:NO];
[blurView setAlpha:1.f];
popupView.layer.transform = transform;
}
completion:^(BOOL finished) {
[self.en_popupViewController viewDidAppear:NO];
if (completionBlock != nil) {
completionBlock();
}
}];
}
- (void)performDismissAnimationInSourceView:(UIView *)sourceView
withBlurView:(JWBlurView *)blurView
popupView:(UIView *)popupView
overlayView:(UIView *)overlayView
completion:(void (^)(void))completionBlock
{
CATransform3D transform = [self transform3d];
[UIView animateWithDuration:kAnimationDuration
animations:^ {
[self.en_popupViewController viewWillDisappear:NO];
[blurView setAlpha:0.f];
popupView.layer.transform = transform;
}
completion:^(BOOL finished) {
[popupView removeFromSuperview];
[blurView removeFromSuperview];
[overlayView removeFromSuperview];
[self.en_popupViewController viewDidDisappear:NO];
self.en_popupViewController = nil;
if (completionBlock != nil) {
completionBlock();
}
}];
}
#pragma mark - Getters
- (UIView*)topView {
UIViewController *recentView = self;
while (recentView.parentViewController != nil) {
recentView = recentView.parentViewController;
}
return recentView.view;
}
#end
I am beginner in ios.Some one please give some solution for my problem.Thanks in advance !
I changed the code of UIViewController+ENPopUp.m for have a visible button.
In code existe a button with screen size for dismiss view. I changed this button and now he is visible.
In presentPopUpView UIViewController+ENPopUp.m where have this comment " // Make the background clickable" I change the code to this:
CGFloat buttonSize = 10;
UIButton * dismissButton = [[UIButton alloc] init];
dismissButton.backgroundColor = [UIColor redColor];
dismissButton.frame = CGRectMake((self.view.frame.size.width/2) - (buttonSize/2),(self.view.frame.size.height/2) + (self.view.frame.size.height/4) , buttonSize, buttonSize);
[overlayView addSubview:dismissButton];
[dismissButton addTarget:self action:#selector(dismissPopUpViewController)
forControlEvents:UIControlEventTouchUpInside];
This create a red button 10x10 like in figure:
Now, you can change the color and the size of button for what you want.
I hope this can help you.

Objective-c OOP is this the best way to reuse my code from father class?

Im new in OOP and I've read some tutorials and books. I have the concept more or less clear but I haven't seen many examples. I've made a simple webView app which only displays a website and got some tabs and push notifications. It's not the big deal. Almost all the code from father class can be reused, and no need to rewrite it, so I've made some inheritance and overriding solutions to childrens, but I'm would like to know if the way I did it is the correct approach to object oriented way, also with the design and even with the code documentation. I need to show this code in a future job interview and I would like to refine it and also learn about experts from here with with more experience than I have.
Any help would be highly appreciated and many thanks!
This is my father class:
//
// FirstViewController.h
#import <UIKit/UIKit.h>
#import "iAd/iAd.h"
#interface FirstViewController : UIViewController <UIWebViewDelegate,UIGestureRecognizerDelegate, ADBannerViewDelegate, UIScrollViewDelegate>
#property (strong, nonatomic) IBOutlet UITabBarItem *tabInicio;
+(void)viewController:(NSString*)link;
-(void)configureView:(UIViewController*)vista;
-(void)iAdDisplay:(UIViewController*)vista;
-(void)autoResizeWhenRotates:(UIViewController*)vista;
-(void)configureWebView:(UIViewController *)vista withUrlLink:(NSURL *)url;
-(void)showHideStatusBar:(UIViewController*)vista;
-(void)addGestures:(UIViewController*)vista;
-(void)configureToolBar:(UIViewController*)vista;
-(void)addIndicator:(UIViewController*)vista;
#end
And the implementation file:
//
// FirstViewController.m
// push
//
#import "FirstViewController.h"
#interface FirstViewController ()
#property UIActivityIndicatorView *indicator;
#property UIToolbar *cToolBar;
#property BOOL isHidden;
#end
#implementation FirstViewController
static UITabBarController *static_Tab = nil;
static UIWebView* static_iVar = nil;
- (void)viewDidLoad {
[super viewDidLoad];
[self configureView:self];
//configure autoresize when device rotate
NSURL *url = [NSURL URLWithString:#"http://hnoslopez.com"];
[self configureStatic_iVar:self withUrlLink:url];
//Set tab bar icons
[[[self.tabBarController.viewControllers objectAtIndex:1] tabBarItem]setImage:[UIImage imageNamed:#"nosotros.png"]];
[[[self.tabBarController.viewControllers objectAtIndex:2] tabBarItem]setImage:[UIImage imageNamed:#"catalogo.png"]];
[[[self.tabBarController.viewControllers objectAtIndex:3] tabBarItem]setImage:[UIImage imageNamed:#"blog.png"]];
self.tabBarController.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.tabBarController.view.autoresizesSubviews = YES;
//Set first tab
_tabInicio = [[UITabBarItem alloc] initWithTitle:#"Inicio" image:[UIImage imageNamed:#"inicioIcon.png"] tag:0];
[[UITabBar appearance] setSelectedImageTintColor:[UIColor greenColor]];
[self.tabBarController setSelectedIndex:0];
self.tabBarItem = _tabInicio;
}
-(void)configureView:(UIViewController *)vista{
//configure toolbar
[self configureToolBar:vista];
//show hide status bar with a gesture recognizer and an animation
[self showHideStatusBar:vista];
//iAd display
[self iAdDisplay:vista];
//auto resizing when device rotates
[self autoResizeWhenRotates:vista];
//Add gestures
[self addGestures:vista];
//Add Indicator
[self addIndicator:vista];
}
-(void)configureToolBar:(UIViewController *)vista{
//Add a toolbar, back and reload button...
UIToolbar *cToolBar = [[UIToolbar alloc] init];
//auto resizing
cToolBar.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingNone;
cToolBar.autoresizesSubviews = YES;
cToolBar.frame = CGRectMake(0, 0, vista.view.frame.size.width, 44);
NSMutableArray *items = [[NSMutableArray alloc] init];
//Set toolbar properties..
[cToolBar setBarStyle:UIBarStyleBlack];
[cToolBar setTranslucent:YES ];
[cToolBar setTintColor:[UIColor greenColor]];
//Back Button..
NSString *backArrowString = #"\U000025C0\U0000FE0E AtrĂ¡s"; //BLACK LEFT-POINTING TRIANGLE PLUS VARIATION SELECTOR
UIBarButtonItem *back = [[UIBarButtonItem alloc] initWithTitle:backArrowString style:UIBarButtonItemStyleDone target:nil action:#selector(goBack)];
//Reload button..
UIBarButtonItem *right = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:nil action:#selector(refreshControl)];
[right setTintColor:[UIColor greenColor]];
//Flexible space between buttons..
UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
[items addObject:back];
[items addObject:flexibleSpace];
[items addObject:right];
[cToolBar setItems:items animated:NO];
[vista.view addSubview:cToolBar];
}
-(void)showHideStatusBar:(UIViewController *)vista{
//Animation performed to hide/show bar when slide your finger..
if ([vista respondsToSelector:#selector(setNeedsStatusBarAppearanceUpdate)]) {
// iOS 7
[vista performSelector:#selector(setNeedsStatusBarAppearanceUpdate)];
} else {
// iOS 6
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationSlide];
}
}
-(void)iAdDisplay:(UIViewController*)vista{
//Display iAd..
[vista setCanDisplayBannerAds:YES];
ADBannerView * adView = [[ADBannerView alloc] initWithFrame:CGRectZero];
adView.frame = CGRectOffset(adView.frame, 0, 44);
adView.delegate = self;
//resizing
adView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
adView.autoresizesSubviews = YES;
[vista.view addSubview:adView];
}
-(void)autoResizeWhenRotates:(UIViewController*)vista{
vista.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
vista.view.autoresizesSubviews = YES;
}
-(void)addGestures:(UIViewController*)vista{
//SWIPE DOWN
UISwipeGestureRecognizer *gest1 = [[UISwipeGestureRecognizer alloc] initWithTarget:vista action:#selector(hideTabBar:)];
gest1.delegate = self;
[gest1 setDirection:UISwipeGestureRecognizerDirectionDown];
[vista.view addGestureRecognizer:gest1];
//SWIPE UP
UISwipeGestureRecognizer *gest2 = [[UISwipeGestureRecognizer alloc] initWithTarget:vista action:#selector(showTabBar:)];
gest2.delegate = self;
[gest2 setDirection:UISwipeGestureRecognizerDirectionUp];
[vista.view addGestureRecognizer:gest2];
}
-(void)addIndicator:(UIViewController *)vista{
//loading indicator
// _indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
UIActivityIndicatorView* indicador = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[indicador setCenter:vista.view.center];
[indicador setHidesWhenStopped:YES];
//resizing
indicador.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
indicador.autoresizesSubviews = YES;
[vista.view addSubview:indicador];
[indicador startAnimating];
}
-(void)configureWebView:(UIViewController *)vista withUrlLink:(NSURL *)url{
//Children method to configure children webViews...
float width = [UIScreen mainScreen].bounds.size.width;
float height = [UIScreen mainScreen].bounds.size.height;
UIWebView *webView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, width,height)];
webView.delegate = self;
//ADD Delegate to disable lateral scroll
webView.scrollView.delegate = self;
//auto resizing
webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
webView.autoresizesSubviews = YES;
NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
[webView loadRequest:requestObj];
[vista.view addSubview:webView];
}
-(void)configureStatic_iVar:(UIViewController *)vista withUrlLink:(NSURL *)url{
//Configure father class webView...
float width = [UIScreen mainScreen].bounds.size.width;
float height = [UIScreen mainScreen].bounds.size.height;
static_iVar = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, width,height)];
static_iVar.delegate = self;
//ADD Delegate to disable lateral scroll
static_iVar.scrollView.delegate = self;
//auto resizing
static_iVar.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
static_iVar.autoresizesSubviews = YES;
NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
[static_iVar loadRequest:requestObj];
[vista.view addSubview:static_iVar];
}
#pragma - mark selectors
-(void)goBack{
NSArray *subviews = [self.view subviews];
for (UIView *subview in subviews){
if([subview isKindOfClass:[UIWebView class]] && [subview respondsToSelector:#selector(goBack)])
{
[subview performSelector:#selector(goBack)];
}
}
}
-(void)refreshControl{
[super viewDidAppear:YES];
//iterate an array of subviews and finds indicator
NSArray *subviews = [self.view subviews];
for (UIView *subview in subviews)
{
if([subview isKindOfClass:[UIActivityIndicatorView class]] && [subview respondsToSelector:#selector(startAnimating)])
{
[subview performSelector:#selector(startAnimating)];
}
if([subview isKindOfClass:[UIWebView class]] && [subview respondsToSelector:#selector(reload)])
{
[subview performSelector:#selector(reload)];
}
}
}
- (void) hideTabBar:(UITabBarController *) tabbarcontroller
{
//Perform animation to hide tab bar
CGRect screenRect = [[UIScreen mainScreen] bounds];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
float fHeight = screenRect.size.height;
for(UIView *view in self.tabBarController.view.subviews)
{
if([view isKindOfClass:[UITabBar class]])
{
[view setFrame:CGRectMake(view.frame.origin.x, fHeight, view.frame.size.width, view.frame.size.height)];
}
}
[UIView commitAnimations];
}
- (void) showTabBar:(UITabBarController *) tabbarcontroller
{
//Perform animation to show tab bar..
CGRect screenRect = [[UIScreen mainScreen] bounds];
float fHeight = screenRect.size.height - self.tabBarController.tabBar.frame.size.height;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
for(UIView *view in self.tabBarController.view.subviews)
{
if([view isKindOfClass:[UITabBar class]] )
{
[view setFrame:CGRectMake(view.frame.origin.x, fHeight, view.frame.size.width, view.frame.size.height)];
}
}
[UIView commitAnimations];
}
+(void)viewController:(NSString*)link{
//Loads push notification links in first tab..
NSURL *url = [NSURL URLWithString:link];
NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
[static_iVar loadRequest:requestObj];
}
#pragma mark - delegate methods
- (void)webViewDidStartLoad:(UIWebView *)webView
{
//iterate an array of subviews and finds indicator
NSArray *subviews = [self.view subviews];
for (UIView *subview in subviews)
{
if([subview isKindOfClass:[UIActivityIndicatorView class]] && [subview respondsToSelector:#selector(startAnimating)])
{
[subview performSelector:#selector(startAnimating)];
}
}
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
//iterate an array of subviews and finds indicator
NSArray *subviews = [self.view subviews];
if([subviews count] == 0)
{
return;
}
else
{
for (UIView *subview in subviews)
{
if([subview isKindOfClass:[UIActivityIndicatorView class]] && [subview respondsToSelector:#selector(stopAnimating)])
{
[subview performSelector:#selector(stopAnimating)];
}
}
}
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
//iterate an array of subviews and finds indicator
NSArray *subviews = [self.view subviews];
if([subviews count] == 0)
{
return;
}
else
{
for (UIView *subview in subviews)
{
if([subview isKindOfClass:[UIActivityIndicatorView class]] && [subview respondsToSelector:#selector(stopAnimating)])
{
[subview performSelector:#selector(stopAnimating)];
}
}
}
CGSize contentSize = webView.scrollView.contentSize;
CGSize viewSize = self.view.bounds.size;
float rw = viewSize.width / contentSize.width;
webView.scrollView.minimumZoomScale = rw;
webView.scrollView.maximumZoomScale = rw;
webView.scrollView.zoomScale = rw;
}
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
return TRUE;
}
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
return YES;
}
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
[scrollView setContentOffset:CGPointMake(0, scrollView.contentOffset.y)];
}
- (BOOL)prefersStatusBarHidden {
return YES;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
And this is a children viewController:
//
// SecondViewController.h
//
#import <UIKit/UIKit.h>
#import "iAd/iAd.h"
#import "FirstViewController.h"
#interface secondViewController : FirstViewController <UIWebViewDelegate,UIGestureRecognizerDelegate,ADBannerViewDelegate, UIScrollViewDelegate>
#property (weak, nonatomic) IBOutlet UIWebView *secondView;
#property (strong, nonatomic) IBOutlet UITabBarItem *secondTab;
#end
Implementation file:
//
// SecondViewController.m
//
//
#import "secondViewController.h"
#import "iAd/iAd.h"
#interface secondViewController ()
#property UIActivityIndicatorView *indicador;
#end
#implementation secondViewController
- (void)viewDidLoad {
[super viewDidLoad];
[super configureView:self];
//configure webView
NSURL *url = [NSURL URLWithString:#"http://hnoslopez.com/nosotros/"];
[self configureWebView:self withUrlLink:url];
//Configure second tab
_secondTab = [[UITabBarItem alloc] initWithTitle:#"Nosotros" image:[UIImage imageNamed:#"nosotros.png"] tag:1];
[[UITabBar appearance] setSelectedImageTintColor:[UIColor greenColor]];
[self.tabBarController setSelectedIndex:1];
self.tabBarItem = _secondTab;
}
- (BOOL)prefersStatusBarHidden {
return YES;
}
-(void)goBack{
//reloads webView
NSArray *subviews = [self.view subviews];
for (UIView *subview in subviews){
if([subview isKindOfClass:[UIWebView class]] && [subview respondsToSelector:#selector(goBack)])
{
[subview performSelector:#selector(goBack)];
}
}
}
-(void)refreshControl{
[super viewDidAppear:YES];
//iterate an array of subviews and finds indicator
NSArray *subviews = [self.view subviews];
if([subviews count] == 0)
{
return;
}
else
{
for (UIView *subview in subviews)
{
//start animating indicator
if([subview isKindOfClass:[UIActivityIndicatorView class]] && [subview respondsToSelector:#selector(startAnimating)])
{
[subview performSelector:#selector(startAnimating)];
}
//reloads webView
if([subview isKindOfClass:[UIWebView class]] && [subview respondsToSelector:#selector(reload)])
{
[subview performSelector:#selector(reload)];
}
}
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)webViewDidStartLoad:(UIWebView *)webView
{
//iterate an array of subviews and finds indicator
NSArray *subviews = [self.view subviews];
if([subviews count] == 0)
{
return;
}
else
{
for (UIView *subview in subviews)
{
if([subview isKindOfClass:[UIActivityIndicatorView class]] && [subview respondsToSelector:#selector(startAnimating)])
{
[subview performSelector:#selector(startAnimating)];
}
}
}
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
//iterate an array of subviews and finds indicator
NSArray *subviews = [self.view subviews];
if([subviews count] == 0)
{
return;
}
else
{
for (UIView *subview in subviews)
{
if([subview isKindOfClass:[UIActivityIndicatorView class]] && [subview respondsToSelector:#selector(stopAnimating)])
{
[subview performSelector:#selector(stopAnimating)];
}
}
}
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
//iterate an array of subviews and finds indicator
NSArray *subviews = [self.view subviews];
if([subviews count] == 0)
{
return;
}
else
{
for (UIView *subview in subviews)
{
if([subview isKindOfClass:[UIActivityIndicatorView class]] && [subview respondsToSelector:#selector(stopAnimating)])
{
[subview performSelector:#selector(stopAnimating)];
}
}
}
CGSize contentSize = webView.scrollView.contentSize;
CGSize viewSize = self.view.bounds.size;
float rw = viewSize.width / contentSize.width;
webView.scrollView.minimumZoomScale = rw;
webView.scrollView.maximumZoomScale = rw;
webView.scrollView.zoomScale = rw;
}
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
return TRUE;
}
- (void) hideTabBar:(UITabBarController *) tabbarcontroller
{
CGRect screenRect = [[UIScreen mainScreen] bounds];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
float fHeight = screenRect.size.height;
for(UIView *view in self.tabBarController.view.subviews)
{
if([view isKindOfClass:[UITabBar class]])
{
[view setFrame:CGRectMake(view.frame.origin.x, fHeight, view.frame.size.width, view.frame.size.height)];
}
}
[UIView commitAnimations];
}
- (void) showTabBar:(UITabBarController *) tabbarcontroller
{
CGRect screenRect = [[UIScreen mainScreen] bounds];
float fHeight = screenRect.size.height - self.tabBarController.tabBar.frame.size.height;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
for(UIView *view in self.tabBarController.view.subviews)
{
if([view isKindOfClass:[UITabBar class]] )
{
[view setFrame:CGRectMake(view.frame.origin.x, fHeight, view.frame.size.width, view.frame.size.height)];
}
}
[UIView commitAnimations];
}
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
return YES;
}
#pragma mark resuelve problema de que la pagina hace scroll horizontal
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
[scrollView setContentOffset:CGPointMake(0, scrollView.contentOffset.y)];
}
#end

UITapGestureRecognizer not working on UIView

I've read multiple solutions here on SO, but they all don;t seem to work in my case.
I've got a class NotificationBar:
.h file:
#interface NotificationBar : UIView <UIGestureRecognizerDelegate>
#property (strong, nonatomic) UILabel *messageLabel;
- (id)initWithFrame:(CGRect)frame message:(NSString *)message;
- (void) show:(int)duration;
#end
.m file:
#import "NotificationBar.h"
#implementation NotificationBar
#synthesize messageLabel;
- (id)initWithFrame:(CGRect)frame message:(NSString *)message
{
self = [super initWithFrame:frame];
if (self)
{
self.alpha = 0;
self.backgroundColor = [UIColor cabmanBlue];
self.userInteractionEnabled = YES;
self.messageLabel = [[UILabel alloc] initWithFrame:CGRectMake(self.bounds.origin.x + 5, 0 , self.bounds.size.width - 10, self.bounds.size.height)];
[self.messageLabel setBackgroundColor:[UIColor clearColor]];
[self.messageLabel setTextColor:[UIColor whiteColor]];
[self.messageLabel setFont:[UIFont boldSystemFontOfSize:14]];
[self.messageLabel setNumberOfLines:2];
self.messageLabel.text = message;
[self addSubview:messageLabel];
UITapGestureRecognizer *tapRec = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(dismiss:)];
tapRec.delegate = self;
tapRec.cancelsTouchesInView = NO;
[self addGestureRecognizer:tapRec];
}
return self;
}
- (void) show:(int)duration
{
[[[[[UIApplication sharedApplication] keyWindow] subviews] lastObject] addSubview:self];
[UIView animateWithDuration:0.5 animations:^{ self.alpha = 1; } completion:^(BOOL finished) {
[UIView animateWithDuration:0.5 delay:duration options:UIViewAnimationOptionCurveLinear animations:^{ self.alpha = 0;} completion:^(BOOL finished)
{
if(finished) [self removeFromSuperview];
}];
}];
}
- (void) dismiss:(UITapGestureRecognizer *)gesture
{
[self.layer removeAllAnimations];
[UIView animateWithDuration:0.5 animations:^{ self.alpha = 0; } completion:^(BOOL finished) {
if(finished)
{
[self removeFromSuperview];
}
}];
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
#end
And I use the class in the following way:
- (void) displayNotificationMessage:(NSString *)message withSound:(BOOL) withSound
{
UIView *topView = [self.window.subviews lastObject];
[[[NotificationBar alloc] initWithFrame:CGRectMake(topView.bounds.origin.x,
64,
topView.bounds.size.width,
40)
message:message] show:10];
if(withSound) AudioServicesPlaySystemSound(1007);
}
The view is always presented and shown. The dismiss function isn't triggered and it seems it doesn't respond to anything. displayNotificationMessage is placed in AppDelegate. displayNotificationMessage is some times used when a viewcontroller with a mapview is displayed or in a UITableViewController. You could say it has to work the same way as UIAlertView: always presented no matter in which screen the user is.
Does anyone see an error or something?
Thank in advance!
Your UILabel takes over almost your entire view bounds:
self.messageLabel = [[UILabel alloc] initWithFrame:CGRectMake(self.bounds.origin.x + 5, 0 , self.bounds.size.width - 10, self.bounds.size.height)];
You may try reducing it's size and then see if the gesture works in the other locations or you can try adding the gesture to the label
[self.messageLabel addGestureRecognizer:singleTap];
I just tried your code on a custom UIView and it works just fine. Having the UILabel as a subview has no effect, it responds well. To me it looks like you probably have either another UIView put over this one (which makes this one buried and therefore unresponsive) or you have another Tap Gesture registered in your superview.

CustomAnnotationView with a button

I've been strunggling with an issue on my project :
I've a mapView and I have to show the annotation presented below (The small (+) is my button)
I've subclassed MKAnnotationView and I have CustomAnnotationView.h like this :
#interface CustomAnnotationView : MKAnnotationView
#property (strong, nonatomic) UIImageView *calloutView;
#property (strong, nonatomic) UIButton *pinButton;
#property (strong, nonatomic) UIView *annView;
- (void)setSelected:(BOOL)selected animated:(BOOL)animated;
- (void)animateCalloutAppearance;
My CustomAnnotationView.m
- (void)setSelected:(BOOL)selected animated:(BOOL)animated{
[super setSelected:selected animated:animated];
if(selected)
{
//building my custom animation
annView = [[ UIView alloc ] initWithFrame:(CGRectMake(0, 0, 30, 30)) ];
annView.frame =CGRectMake(0, 0, 200, 50);
calloutView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"customcallout.png"]];
[calloutView setFrame:CGRectMake(-25, 50, 0, 0)];
[calloutView sizeToFit];
[self animateCalloutAppearance];
//I tried to add a button here but I could't do it like it should be
[annView addSubview:calloutView];
[self addSubview:annView];
}
else
{
//Remove your custom view...
[annView removeFromSuperview];
}
}
- (void)didAddSubview:(UIView *)subview{
if ([[[subview class] description] isEqualToString:#"UICalloutView"]) {
for (UIView *subsubView in subview.subviews) {
if ([subsubView class] == [UIImageView class]) {
UIImageView *imageView = ((UIImageView *)subsubView);
[imageView removeFromSuperview];
}else if ([subsubView class] == [UILabel class]) {
UILabel *labelView = ((UILabel *)subsubView);
[labelView removeFromSuperview];
}
}
}
}
- (void)animateCalloutAppearance {
CGFloat scale = 0.001f;
calloutView.transform = CGAffineTransformMake(scale, 0.0f, 0.0f, scale, 0, -50);
[UIView animateWithDuration:0.15 delay:0 options:UIViewAnimationCurveEaseOut animations:^{
CGFloat scale = 1.1f;
calloutView.transform = CGAffineTransformMake(scale, 0.0f, 0.0f, scale, 0, 2);
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.1 delay:0 options:UIViewAnimationCurveEaseInOut animations:^{
CGFloat scale = 0.95;
calloutView.transform = CGAffineTransformMake(scale, 0.0f, 0.0f, scale, 0, -2);
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.075 delay:0 options:UIViewAnimationCurveEaseInOut animations:^{
CGFloat scale = 1.0;
calloutView.transform = CGAffineTransformMake(scale, 0.0f, 0.0f, scale, 0, 0);
} completion:nil];
}];
}];
}
#end
With this I'm able to show my custom annotation on the map but I can't figure out how to place a button on it and this button should of course be able to respond to clicks like the callback calloutAccessoryControlTapped:
Please anyone with a working example code or idea.
Thank you.
I solved this using - (UIView*)hitTest in my CustmAnnotationView.m class. I have a separate nib for the callout view and use hittest to determine whether the UILabel named directionsButton (linked from the callout nib) was clicked:
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
if (selected) {
calloutVisible = YES;
hitTestBuffer = NO;
self.calloutView = [[[NSBundle mainBundle] loadNibNamed:#"CalloutView" owner:self options:nil] objectAtIndex:0];
[directionsButton setFont:[UIFont fontWithName:#"DIN-Medium" size:12.0f]];
[calloutView setFrame:CGRectMake(calloutView.frame.origin.x, calloutView.frame.origin.y, 280, calloutView.frame.size.height)];
[calloutView setAlpha:0.0f];
[self addSubview:calloutView];
[self sendSubviewToBack:calloutView];
[UIView animateWithDuration:0.3f delay:0.0f options:0 animations:^{
[calloutView setAlpha:1.0f];
} completion:^(BOOL finished) {
}];
[super setSelected:selected animated:animated];
} else {
calloutVisible = NO;
CGRect newFrame = self.frame;
newFrame.size.width = 52;
self.frame = newFrame;
[UIView animateWithDuration:0.3f delay:0.0f options:0 animations:^{
[calloutView setAlpha:0.0f];
} completion:^(BOOL finished) {
[calloutView removeFromSuperview];
}];
}
}
- (void)directionsPressed
{
if ([parent respondsToSelector:#selector(directionsPressed:)])
[parent performSelector:#selector(directionsPressed:) withObject:self.stockist];
}
- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
if (calloutVisible) {
CGPoint hitPoint = [calloutView convertPoint:point toView:directionsButton];
if ([calloutView pointInside:hitPoint withEvent:event]) {
if (!hitTestBuffer) {
[self directionsPressed];
hitTestBuffer = YES;
}
return [directionsButton hitTest:point withEvent:event];
} else {
hitTestBuffer = NO;
}
}
return [super hitTest:point withEvent:event];
}
In my code parent is my current VC. This is probably not the neatest way but it worked at the time.
The callout looks like this:
A couple of booleans calloutVisible and hitTestBuffer make sure the button is only clickable once and only when visible.
To try and resolve your issue, I played a bit with Apple's WeatherMap example, and came up with a working solution to your problem.
I added your code to the WeatherAnnotationView.m file (minus the animation code), and on top of that added the code to display the button, and respond to touch events:
-(void)annotationButtonClicked{
NSLog(#"** button clicked");
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated{
if(selected)
{
//building my custom animation
annView = [[ UIView alloc ] initWithFrame:(CGRectMake(0, 0, 30, 30)) ];
annView.frame =CGRectMake(0, 0, 200, 50);
calloutView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"cloudy.png"]];
[calloutView setFrame:CGRectMake(-25, 50, 0, 0)];
[calloutView sizeToFit];
[annView addSubview:calloutView];
/* BUTTON CODE */
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0,0,20,20)];
button.backgroundColor = [UIColor orangeColor];
[button addTarget:self action:#selector(annotationButtonClicked) forControlEvents:UIControlEventTouchUpInside];
[annView addSubview:button];
/* END Of BUTTON CODE */
[self addSubview:annView];
}
else
{
//Remove your custom view...
[annView removeFromSuperview];
}
}
You haven't posted HOW you tried to add a button to the annView, so it is hard for me to understand what was not working with your approach. A common approach for these kind of problems is to remove one piece of code at a time and try to run your app. For starters, try to remove the animation code. Then, try to remove calloutView, and remain only with annView and the button, until you have code that works with a button. After that, add back the pieces you removed earlier (animation, calloutView) one at a time and see which one is causing your button to stop function.
I hope this helps somehow.

Resources