I have created a view for custom callout with a button. This custom callout gets displayed on click of any annotation on Mapview. But nothing happens when I click the button inside the custom callout.
#import <UIKit/UIKit.h>
#interface TestView : UIView
#end
#import "TestView.h"
#implementation TestView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
-(IBAction)showMessage:(id)sender
{
NSLog(#"Test");
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
#end
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view {
if(![view.annotation isKindOfClass:[MKUserLocation class]]) {
CGRect rect = CGRectMake(0,0,268,140);
TestView *testview = [[TestView alloc] initWithFrame:rect];
testview = [self loadNibNamed:#"TestView" ofClass:[TestView class]];
[view addSubview:testview];
}
}
The view is loaded using loadNibNamed method.
- (id)loadNibNamed:(NSString *)nibName ofClass:(Class)objClass {
if (nibName && objClass) {
NSArray *objects = [[NSBundle mainBundle] loadNibNamed:nibName owner:nil options:nil];
for (id currentObject in objects ){
if ([currentObject isKindOfClass:objClass])
return currentObject;
}
}
return nil;
}
I have checked the userInteractionEnabled property for View and Button and both are set to true.
Also mapped the showMessage with Touch Up Inside event of button.
any suggestion on how I go about identifying this problem?
Try using the following static method to get TestView
+ (TestView *)getNewTestView {
TestView *view = nil;
NSArray *xib = [[NSBundle mainBundle] loadNibNamed:#"TestView" owner:nil options:nil];
for (NSObject *obj in xib) {
if ([obj isKindOfClass:[TestView class]]) {
view = (TestView *)obj;
}
}
return view;
}
And then, to get an instance of the object, use:
TestView *testView = [TestView getNewTestView];
[self.view addSubview:testView];
Please make sure your MKAnnotationView has enough space to fill testView in.
If it's smaller than testView, the outer space would not respond to any actions.
You can set clipsToBounds to NO to check out the actual space.
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view {
if(![view.annotation isKindOfClass:[MKUserLocation class]]) {
view.clipsToBounds = NO;
CGRect rect = CGRectMake(0,0,268,140);
TestView *testview = [[TestView alloc] initWithFrame:rect];
testview = [self loadNibNamed:#"TestView" ofClass:[TestView class]];
[view addSubview:testview];
}
}
Related
So, I've created a custom callout that appears over the selected annotation. Everything works exactly how I want--the only problem is after I've dropped another pin of the same type, then all the past pins will display the same information. For example, I may have a pin drop that displays in the callout "McDonalds" and the next pin that drops displays "Starbucks". Now all my previously dropped pins will display "Starbucks".
I'm currently attempting to store information in the annotation's subtitle property and then displaying that through a formatted string onto my custom UIView's Label...It works but I need the annotation's information to never change. There must be something I'm missing or do not understand. Any help will be much appreciated.
I've posted all the code I believe to be relevant below. Thank you!
Custom Callout.h
#import <UIKit/UIKit.h>
#interface PinView : UIView
{
UIView *view;
UILabel *theValueLabel;
}
#property (nonatomic, retain) IBOutlet UIView *view;
#property (nonatomic, retain) IBOutlet UILabel *theValueLabel;
#end
Custom Callout.m
#import "PinView.h"
#implementation PinView
#synthesize theValueLabel;
#synthesize view;
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
UIView *nib = [[[UINib nibWithNibName:#"customView" bundle:nil] instantiateWithOwner:self options:nil] objectAtIndex:0];
[self addSubview:nib];
}
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
if (self.subviews.count == 0) {
UIView *nib = [[[UINib nibWithNibName:#"customView" bundle:nil] instantiateWithOwner:self options:nil] objectAtIndex:0];
[self addSubview:nib];
}
}
return self;
}
- (void)awakeFromNib {
[super awakeFromNib];
[self setup];
}
- (void)setup {
self.theValueLabel.text = #"foo";
}
in my main View Controller.m
#interface BreadTrailViewController ()<CLLocationManagerDelegate, MKMapViewDelegate, MFMailComposeViewControllerDelegate>
{
PinView *aView
MKPointAnnotation *Pin1;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation *location = [locations lastObject];
if (location != nil)
{
Pin1 = [[MKPointAnnotation alloc] init];
Pin1.title = #"Venue";
Pin1.subtitle = [NSString stringWithFormat:#"Venue Name: %#\n%f,%f\nAddress: %#",revGeocodeVenue, lat, lng, revGeocodeAddress];
Pin1.coordinate = location.coordinate;
[self.mapView addAnnotation:Pin1];
}
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
// If it's the user location, just return nil.
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
// Handle any custom annotations.
if ([annotation isKindOfClass:[MKPointAnnotation class]])
{
MKPinAnnotationView *pinView = (MKPinAnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:#"CustomPinAnnotationView"];
if (!pinView)
{
pinView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"CustomPinAnnotationView"];
pinView.canShowCallout = NO;
} else {
pinView.annotation = annotation;
}
if ([[annotation title] containsString:#"Venue"])
{
pinView.pinTintColor = [UIColor greenColor];
}
return pinView;
}
return nil;
}
-(void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
aView = [[PinView alloc] initWithFrame:CGRectMake( 0, 0, 300, 350)];
aView.layer.cornerRadius = 20;
aView.layer.masksToBounds = YES;
aView.center = CGPointMake(view.bounds.size.width*0.5f, -aView.bounds.size.height*0.35f);
//Using the Pin's subtitle to store the data string and display on PinView
if ([[view.annotation title] containsString:#"Venue"])
{
aView.theValueLabel.text = Pin1.subtitle;
}
[view addSubview:aView];
}
-(void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view
{
[aView removeFromSuperview];
}
I found the problem! I was using Pin1.subtitle when what I should be using is view.annotation.subtitle--everything is working perfectly now. I hope this helps someone else!
-(void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
aView = [[PinView alloc] initWithFrame:CGRectMake( 0, 0, 300, 350)];
aView.layer.cornerRadius = 20;
aView.layer.masksToBounds = YES;
aView.center = CGPointMake(view.bounds.size.width*0.5f, -aView.bounds.size.height*0.35f);
//Using the Pin's subtitle to store the data string and display on PinView
if ([[view.annotation title] containsString:#"Venue"])
{
aView.theValueLabel.text = view.annotation.subtitle;
}
[view addSubview:aView];
}
i have created a UIView .xib with some labels and a button. I connected all the outlets and the IBAction for the button.
When i add the .xib subview from my viewcontroller it shows ok, but the button is not working.
This is my xib´s .m file
-(instancetype)initWithCoder:(NSCoder *)aDecoder
{
if(self = [super initWithCoder:aDecoder])
{
[self load];
}
return self;
}
-(instancetype)initWithFrame:(CGRect)frame
{
if(self = [super initWithFrame:frame])
{
[self load];
}
return self;
}
-(void)load
{
[[self superview] setUserInteractionEnabled:YES];
[self setUserInteractionEnabled:YES];
UIView *view = [[[NSBundle bundleForClass:[self class]] loadNibNamed:#"AgregarAlCarrito" owner:self options:nil] firstObject];
[self addSubview:view];
view.frame = self.bounds;
}
i have tried everything you can imagine without results
thanks
So this line looks very suspect to me:
[self addSubview:view];
You should be adding the xib view to the parent view. Where I did something like this in one of my apps, I had something like this:
[[self view] addSubview:_xibView];
I'm using a custom UIView in myViewController. I have a SampleView which is my custom view. I have my class as..
SampleView.h
#property (strong, nonatomic) IBOutlet UIView *mainView;
#property (strong, nonatomic) IBOutlet UIButton *sampleButton;
and in the SampleView.m
I have added a basic init
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
[self commonInit];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self commonInit];
}
return self;
}
- (void)commonInit
{
UIView *view = nil;
NSArray *objects = [[NSBundle mainBundle] loadNibNamed:#"SampleView"
owner:self
options:nil];
for (id object in objects) {
if ([object isKindOfClass:[UIView class]]) {
view = object;
break;
}
}
if (view != nil) {
_mainView = view;
view.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:view];
[self setNeedsUpdateConstraints];
}
}
Now in my ViewController I want to change the property of the title on the sampleButton...
SampleView *sampleView = [[SampleView alloc]init];
sampleView.sampleButton.titleLabel.text = #"Hello";
[self.view addSubview:sampleView];
There is no change in the SubView's button text. Please do help me about the same.
Thanks.
I think
- (void)commonInit
{
//UIView *view = nil;
UIView *view=[[UIView all]init];
........
}
I am trying to subclass a UIView using the nib. Using the following code:
- (void)awakeFromNib
{
[super awakeFromNib];
NSArray *v = [[NSBundle mainBundle] loadNibNamed:#"Qus_Scale1to7View" owner:self options:nil];
[self addSubview:[v objectAtIndex:0]];
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
NSArray *v = [[NSBundle mainBundle] loadNibNamed:#"Qus_Scale1to7View" owner:self options:nil];
[self addSubview:[v objectAtIndex:0]];
}
return self;
}
this creates the object correctly and view also displayed and when the object loads from its nib the delegate instantly becomes null and ignores any attempt to assign values to it.
Can anyone know the reason why it is?
Thanks in advance.
It won't work reusing same xib for multiple view controllers. If you want to reuse that view make a class that inherits from UIView and add the code there.
#import "SomeProtocol.h"
#interface MyCustomView : UIView {
IBOutlet UIView *headerView;
IBOutlet UIView *footerView;
IBOutlet UIButton *updateBtn;
}
#property (nonatomic, assign) id<SomeProtocol> delegate;
#end
............
#implementation BCFirmwareView
#synthesize delegate = _delegate;
+ (id)viewFromNibWithName: (NSString*)name {
UIView *view = nil;
NSArray *views = [[NSBundle mainBundle] loadNibNamed: name owner: self options: nil];
if (views) {
for (UIView *aView in views) {
if ([aView isKindOfClass: NSClassFromString(name)])
view = aView;
}
}
return view;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder: aDecoder];
if (self) {
}
return self;
}
- (id)init {
self = [[MyCustomView viewFromNibWithName: #"MyCustomView"] retain];
if (self) {
}
return self;
}
- (void)dealloc {
self.delegate = nil;
[headerView release];
[footerView release];
[updateBtn release];
[super dealloc];
}
- (void)awakeFromNib {
[super awakeFromNib];
// Do any additional setup after loading the view from its nib.
headerView.backgroundColor = [UIColor redColor];
footerView.backgroundColor = [UIColor greenColor];
}
- (void)willMoveToSuperview:(UIView *)newSuperview {
[super willMoveToSuperview: newSuperview];
if (!newSuperview)
return;
}
- (void)didMoveToSuperview {
[super didMoveToSuperview];
}
- (IBAction)updateBtnPressed: (id)sender {
// do some stuff
}
#end
The next step is to open the xib in Interface Builder and set your class as the custom class for the view, not for the File's Responder. Right click on the view and make the outlet and actions connections.
Now you should be able to simply make instances of your MyCustomView in any view controller and use it. Will work from Interface Builder as well if you don't forget to change your view custom class to your class.
You can create a custom UIView with Xib and add properties to it.
Then you link the class with the xib and link the properties with the IB.
Or you can only use the
NSArray *v = [[NSBundle mainBundle] loadNibNamed:#"Qus_Scale1to7View" owner:self options:nil];
UIView *view = [v objectAtIndex:0];
and set your objects values using viewWithTag: method.
UILabel *label = (UILabel *)[view viewWithTag:yourTag];
Let me know if this helps.
In iOS for the iPhone I want to make a control with similar appearance and behavior to the android spinner control when configured to behave like a drop down list box. Specifically when pressed a modal list of text options with radio buttons comes up and when one of them is pressed the list disappears and the control updates to that choice. Example:
So far I have seen a full-screen option using [self presentViewController...] with a custom ViewController but I want a partial screen (like pictured above) solution. Does anyone know how to do this or could point in the right direction.
The native solution to this will be a UIActionSheet which on iPhone will appear from the bottom and be partial screen or on iPad be very similar to the android version.
You can find the documentation here: UIActionSheet
if you didnt want to use the UIActionSheet and you wanted to make it reusable rather than adding a whole bund of UIViews to your current XIB, you could create a custom UIView with whatever interface you would need to populate it and use the interface builder to help make it look ok.
that view could have a message handler that posts the response that you would need to listen for.
then just init and load the view into your subviews and populate it
then post a message from the custom view to the handler you registered
so for your custom view you would have something like this.
#implementation SomeCustomView
+(SomeCustomView*)viewFromNibNamed:(NSString *)nibName{
NSArray *nibContents = [[NSBundle mainBundle] loadNibNamed:nibName owner:self options:NULL];
NSEnumerator *nibEnumerator = [nibContents objectEnumerator];
SomeCustomView *customView = nil;
NSObject* nibItem = nil;
while ((nibItem = [nibEnumerator nextObject]) != nil) {
if ([nibItem isKindOfClass:[AADropDown class]]) {
customView = (SomeCustomView*)nibItem;
break;
}
}
return customView;
}
-(void)someInitializationWith:(NSArray*)repeatableData andNotificationId:(NSString*)noteId{
//set your stuff up for the view here and save the notification id
}
...
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
[[NSNotificationCenter defaultCenter] postNotificationName:Your_Notification_Id object:somevalue];
}
#end
and include other things, like in this case the tableview stuff or any other logic.
then in your viewcontroller you could call it like
__block id observer = [[NSNotificationCenter defaultCenter] addObserverForName:#"customViewAction" object:nil queue:[NSOperationQueue currentQueue] usingBlock:^(NSNotification *note) {
//deal with notification here
[[NSNotificationCenter defaultCenter] removeObserver: observer];
}];
SomeCustomView *cv =(SomeCustomView*) [SomeCustomView viewFromNibNamed:#"SomeCustomView"];
[cv someInitializationWith:arrayOptions andNotificationId:#"customViewAction"];
[self.view addSubview:cv];
and in your interface builder you will just need to make sure that the class of the view is set to your class type.
then you can easily reuse this code again whenever a user needs to select something else in the same manner.
Here is a variation on the solution suggested by AtomRiot.
On your view (xib or storyboard) make a button and assign this graphic to it. Don't worry if it appears stretched out in the editor. The code will make it a realizable graphic.
2X version
Then include the following files in your project (copied below):
DDLBHelper.h
DDLBHelper.m
Then in your ViewController's .h file make links to the button:
#property (weak, nonatomic) IBOutlet UIButton *ddlbB;
- (IBAction)ddlbBClick:(id)sender;
In you ViewController's .m file make the following calls:
#synthesize ddlbB, choiceLabel;
DDLBHelper *mDDLBH;
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *strings = [[NSArray alloc] initWithObjects:#"Item 1", #"Item 2", #"Item 3", nil];
mDDLBH = [[DDLBHelper alloc] initWithWithViewController:self button:ddlbB stringArray:strings currentValue:1];
}
- (IBAction)ddlbBClick:(id)sender {
[mDDLBH popupList];
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{
[mDDLBH adjustToRotation];
}
Works just like android.
Here are the files:
DDLBHelper.h
// DDLBHelper.h
// Created by MindSpiker on 9/27/12.
#import <Foundation/Foundation.h>
#protocol DDLBHelperDelegate <NSObject>
#required
- (void) itemSelected: (int)value;
#end
#interface DDLBHelper : UIViewController <UITableViewDelegate, UITableViewDataSource>{
id <DDLBHelperDelegate> delegate;
}
#property (retain) id delegate;
// external interface
- (id) init;
- (id) initWithWithViewController:(UIViewController *)viewController button:(UIButton *)button stringArray:(NSArray *)values currentValue:(int) currentValue;
- (void) popupList;
- (BOOL) isShown;
- (void) adjustToRotation;
- (int) getValue;
- (NSString *)getValueText;
#end
DDLBHelper.m
// DDLBHelper.m
// Created by MindSpiker on 9/27/12.
#import "DDLBHelper.h"
#import <QuartzCore/QuartzCore.h>
#interface DDLBHelper () {
#private
UIViewController *mVC;
UIButton *mButton;
NSArray *mValues;
int mValue;
UITableView *mTV;
UIView *mBackgroundV;
}
#end
#implementation DDLBHelper
#synthesize delegate;
- (id) init {
self = [super init];
mVC = nil;
mButton = nil;
mValues = nil;
mValue = -1;
return self;
}
- (id) initWithWithViewController:(UIViewController *)viewController button:(UIButton *)button stringArray:(NSArray *)values currentValue:(int) currentValue {
self = [super init];
// save pointers
mVC = viewController;
mButton = button;
mValues = values;
mValue = currentValue;
[self setupButton];
return self;
}
- (void) popupList{
if (mBackgroundV == nil){
mBackgroundV = [self setupBackgroundView];
[mVC.view addSubview:mBackgroundV];
}
if (mTV == nil){
mTV = [self setupTableView];
[mVC.view addSubview:mTV];
}
[mTV reloadData];
[mBackgroundV setHidden:NO];
[mTV setHidden:NO];
}
- (BOOL) isShown{
return !mTV.isHidden;
}
- (void) adjustToRotation{
BOOL isShown = [self isShown];
// remove the controls
if (mBackgroundV != nil){
[mBackgroundV removeFromSuperview];
mBackgroundV = nil;
}
if (mTV != nil){
[mTV removeFromSuperview];
mTV = nil;
}
if (isShown){
[self popupList];
}
}
- (int) getValue{
return mValue;
}
- (NSString *) getValueText{
if (mValues != nil && mValue > -1) {
if (mValues.count > mValue){
return [mValues objectAtIndex:mValue];
}
}
return nil;
}
- (void) updateButtonTitle{
NSString *title = [NSString stringWithFormat:#" %#", [self getValueText]];
[mButton setTitle:title forState:UIControlStateNormal];
}
- (void) setupButton {
UIImage *buttonBG = [UIImage imageNamed:#"sis_proceeds_ddlb.png"];
UIEdgeInsets insets = UIEdgeInsetsMake(8, 8, 8, 45);
UIImage *sizableImg = [buttonBG resizableImageWithCapInsets:insets];
[mButton setBackgroundImage:sizableImg forState:UIControlStateNormal];
[mButton setContentHorizontalAlignment:UIControlContentHorizontalAlignmentLeft];
[self updateButtonTitle];
}
- (UIView *) setupBackgroundView{
UIView *v = [[UIView alloc] initWithFrame:mVC.view.bounds];
[[v layer] setOpaque:NO];
[[v layer] setOpacity:0.7f];
[[v layer] setBackgroundColor:[UIColor blackColor].CGColor];
return v;
}
- (UITableView *) setupTableView {
CGRect rect = [self makeTableViewRect];
UITableView *tv = [[UITableView alloc] initWithFrame:rect style:UITableViewStylePlain];
[tv setDelegate:self];
[tv setDataSource:self];
[tv setBackgroundColor:[UIColor whiteColor]];
[[tv layer] setBorderWidth:2];
[[tv layer] setBorderColor:[UIColor lightGrayColor].CGColor];
[[tv layer] setCornerRadius:10];
[mVC.view addSubview:tv];
return tv;
}
- (CGRect) makeTableViewRect {
float l=0.0, t=0.0, w=0.0, h=0.0, maxH=0.0, cellH=0.0, cellsH=0.0;
// get
l = mButton.frame.origin.x;
w = mButton.frame.size.width;
t = mVC.view.bounds.origin.y + 50;
maxH = mVC.view.bounds.size.height - 100;
// get cell height
UITableViewCell *c = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
cellH = c.bounds.size.height;
// see if list will overlow maxH(eight)
cellsH = cellH * mValues.count;
if (cellsH > maxH) {
h = maxH;
} else {
h = cellsH;
}
return CGRectMake(l, t, w, h);
}
#pragma mark - TableView Delegate functions
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1; // this is a one section table
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return mValues.count; // should be called for only one section
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
// try to resuse a cell if possible
static NSString *RESUSE_IDENTIFIER = #"myResuseIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:RESUSE_IDENTIFIER];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:RESUSE_IDENTIFIER];
}
cell.textLabel.text = [mValues objectAtIndex:indexPath.row];
if (mValue == indexPath.row){
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
// save value and hide view
mValue = indexPath.row;
[self updateButtonTitle];
[mBackgroundV setHidden:YES];
[mTV setHidden:YES];
[delegate itemSelected:mValue];
}
#end