Sorry, I have read a bunch of tutorials how to create a custom Callout for MapKit Annotation. It works with NSLog, but I cannot display the information in the Callouts.
I have two type of icons on the map. This is my viewForAnnotation method:
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
if (! [annotation isKindOfClass:[IGAMapAnnotation class]]) {
return nil;
}
IGAMapAnnotation *myLocation = (IGAMapAnnotation *) annotation;
self.typeIsFix = [myLocation.navaidType isEqualToString:#"FIX"];
self.typeIsPort = [myLocation.navaidType isEqualToString:#"PORT"];
int planeImageViewTag = 42;
NSString *reuseId;
if (self.typeIsPort)
reuseId = #"IGAMapAnnotationPort";
else if (self.typeIsFix)
reuseId = #"IGAMapAnnotationFix";
else
reuseId = #"IGAMapAnnotationOther";
MKAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:reuseId];
if (annotationView == nil)
{
annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:reuseId];
annotationView.enabled = YES;
UIButton *annotationInfo = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
annotationView.rightCalloutAccessoryView = annotationInfo;
annotationView.canShowCallout = YES;
if (self.typeIsPort)
{
annotationView.image = [UIImage imageNamed:#"mapPORT.png"];
annotationView.centerOffset = CGPointMake(0, 0);
}
else if (self.typeIsFix)
{
annotationView.image = [UIImage imageNamed:#"mapFIX.png"];
annotationView.centerOffset = CGPointMake(0, 0);
}
else
return nil;
}
else
{
annotationView.annotation = annotation;
}
return annotationView;
}
Then I have a calloutAccessoryControlTapped method
- (void)mapView:(MKMapView *)mapview annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
IGAAnnotationInfoViewController *popOverCallout = [[IGAAnnotationInfoViewController alloc]init];
UIPopoverController *popOver = [[UIPopoverController alloc] initWithContentViewController:popOverCallout];
popOver.popoverContentSize = CGSizeMake(300, 200);
[popOver presentPopoverFromRect:view.bounds
inView:view
permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
I have also created a UIViewController which I assigned to UIPopoverController.
Now, when I tap the button on my annotation I see a white space for text. Great. If I assign text to a label in UIViewController, it also works great (the following is my UIViewController.m):
- (void)viewDidLoad {
[super viewDidLoad];
txtCallout = [[UILabel alloc] initWithFrame:CGRectMake(0.0, 0.0, 300, 200) ];
txtCallout.font = [UIFont fontWithName:#"Arial Rounded MT Bold" size:(14.0)];
txtCallout.numberOfLines = 0;
txtCallout.clipsToBounds = YES;
txtCallout.backgroundColor = [UIColor clearColor];
txtCallout.textColor = [UIColor blackColor];
txtCallout.textAlignment = NSTextAlignmentLeft;
txtCallout.text = #"text\ntext\ntext";
[self.view addSubview:txtCallout];
}
But how do I insert the text from my annotation method? Also the text must be different depending on the icon type, say #"PORT, PORT" or #"FIX,FIX". How do I do it?
EDIT:
I have managed to display callouts with the necessary information passed. My last problem is that sometimes my callout is 3 lines, sometimes -- as many as 15. How is it possible to make the callout adjust automatically to the number of lines in my string? Should I modify my popoverContentSize or my label size in the UIViewController?
Thank you so much!
I have figured out how to adjust the MK Annotation callout to a UILabel.
Implement the calloutAccessoryControlTapped method
- (void)mapView:(MKMapView *)mapview annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
// OPTIONAL: Deselecting Annotation View when Callout is tapped
//[mapview deselectAnnotation:view.annotation animated:YES];
NSString *calloutDetails;
IGAMapAnnotation *annotationTapped = (IGAMapAnnotation *)view.annotation;
calloutDetails = [NSString stringWithFormat:#"YOUR TEXT:\nYOURTEXT\n"];
// Declare and initialize the UIViewController that has the label to contain the callout information
IGAAnnotationInfoViewController *detailViewController = [[IGAAnnotationInfoViewController alloc]initWithText:calloutDetails];
UIPopoverController *popOver = [[UIPopoverController alloc] initWithContentViewController:detailViewController];
// Size of the UIPopoverController = size of the label + 40 pts
popOver.popoverContentSize = CGSizeMake(detailViewController.txtCallout.frame.size.width+40,detailViewController.txtCallout.frame.size.height+40);
// Show popover controller
[popOver presentPopoverFromRect:view.bounds
inView:view
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:YES];
}
Now, IGAAnnotationInfoViewController.h
#interface IGAAnnotationInfoViewController : UIViewController {
CGRect calloutSize;
}
#property (strong,nonatomic) NSString *calloutInformation;
#property (strong,nonatomic) IGACalloutLabel *txtCallout;
-(IGAAnnotationInfoViewController*) initWithText : (NSString*) calloutText;
IGAAnnotationInfoViewController.m
#implementation IGAAnnotationInfoViewController
#synthesize calloutInformation,txtCallout;
-(IGAAnnotationInfoViewController*) initWithText : (NSString*) calloutText {
self = [super init];
if ( self ) {
calloutInformation = calloutText;
// Creating a label that will display the callout information (passed from IGAAcarasViewController - Map Annotation)
txtCallout = [[IGACalloutLabel alloc] initWithFrame:CGRectMake(20, 20, 0, 0)];
txtCallout.lineBreakMode = NSLineBreakByWordWrapping;
txtCallout.numberOfLines=0;
txtCallout.backgroundColor = [UIColor clearColor];
txtCallout.textColor=[UIColor blueColor];
txtCallout.text = calloutInformation;
[txtCallout drawTextInRect:CGRectMake(10,10,0,0)];
[txtCallout sizeToFit];
[self.view addSubview:txtCallout];
}
return self;
}
Finally, subclass the UILabel class:
implementation IGACalloutLabel
#synthesize topInset, leftInset, bottomInset, rightInset;
- (void)drawTextInRect:(CGRect)rect
{
UIEdgeInsets insets = {topInset,leftInset,bottomInset,rightInset};
return [super drawTextInRect:UIEdgeInsetsInsetRect(rect, insets)];
}
Regards,
Related
I have been trying to figure this issue out for days now with no luck, any advice would be greatly appreciated.
I have a uitableviewcontroller that loads data from core data. When the tableview loads, the first 6 (no matter how many objects are actually saved) are loaded. When I begin to scroll down, all of the following cells are labeled "(null)". When I go back up to the top, the data in the original 6 cells are replaced with "(null)". When I logged the contents of the array from the fetch, all of the objects from core data get logged, but when I log the contents of the cell, the first 6 contents get logged, the a long list of rows with (null) get logged.
I've tried a lot of differnet things to debug this, but nothing has worked so far.
Here is the tableviewcontroller file
-(void)viewWillAppear:(BOOL)animated
{
[self setTitle:#"My Safe"];
self.dictionaryWithContent = [[NSMutableDictionary alloc] init];
self.search = #0;
[self fetchDataFromCoreData];
[self.tableView setFrame:CGRectMake(0, 88, self.tableView.bounds.size.width, self.tableView.bounds.size.height)];
self.searchResults = [[NSMutableArray alloc] init];
self.tableView.tableHeaderView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, self.tableView.bounds.size.width, 44.0f)];
UIBarButtonItem *changeViewsButton = [[UIBarButtonItem alloc] initWithTitle:#"Tweets By User"
style:UIBarButtonItemStylePlain
target:self
action:#selector(switchViewControllers)];
self.navigationItem.rightBarButtonItem = changeViewsButton;
self.tableView.delegate = self;
self.tableView.dataSource = self;
// CGRect searchView = CGRectMake(0, 44, self.tableView.bounds.size.width, 44);
self.searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
self.searchController.searchResultsUpdater = self;
[self.searchController loadViewIfNeeded];
self.tableView.tableHeaderView = self.searchController.searchBar;
self.searchController.dimsBackgroundDuringPresentation = NO;
self.searchController.searchBar.barTintColor = twitter_blue;
self.searchController.delegate = self;
self.searchController.searchBar.delegate = self;
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:animated];
self.navigationController.navigationBar.barTintColor = twitter_blue;
self.navigationController.navigationBar.translucent = NO;
self.navigationController.navigationBar.tintColor = [UIColor whiteColor];
[self.navigationController.navigationBar setTitleTextAttributes:#{NSForegroundColorAttributeName : [UIColor whiteColor]}];
}
-(void)fetchDataFromCoreData
{
AppDelegate *del = [[AppDelegate alloc] init];
NSManagedObjectContext *context = del.managedObjectContext;
NSString *entity;
entity = [NSString stringWithFormat:#"Tweet"];
NSFetchRequest *fet = [NSFetchRequest fetchRequestWithEntityName:entity];
NSError *e;
NSArray *array = [context executeFetchRequest:fet error:&e];
self.arrayWithContent = [[NSArray alloc] initWithArray:array];
for (Tweet *t in self.arrayWithContent) {
NSLog(#"array %#", t.messageOfTweet);
}
}
-(void)switchViewControllers
{
PCRSavedTweetByUserTableViewController *vc = [[PCRSavedTweetByUserTableViewController alloc] init];
PCRNavigationBarController *navBarControllerOfSafeSide = [[PCRNavigationBarController alloc] initWithRootViewController:vc];
[self.navigationController presentViewController:navBarControllerOfSafeSide animated:YES completion:nil];
}
#pragma mark Status bar
- (void)willPresentSearchController:(UISearchController *)searchController {
// do something before the search controller is presented
self.navigationController.navigationBar.translucent = YES;
}
-(void)willDismissSearchController:(UISearchController *)searchController
{
self.navigationController.navigationBar.translucent = NO;
searchController = self.searchController;
}
-(void)updateSearchResultsForSearchController:(UISearchController *)searchController
{
searchController = self.searchController;
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
{
self.search = #1;
[self.searchResults removeAllObjects];
NSString *searchBarString = self.searchController.searchBar.text;
for (Tweet *tweet in self.arrayWithContent){
if ([tweet.messageOfTweet containsString:searchBarString] || [tweet.userNameOfTweetUser containsString:searchBarString] || [tweet.whoPosted.name containsString:searchBarString]) {
[self.searchResults addObject:tweet];
}
}
[self.tableView reloadData];
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
{
[self.searchController.searchBar resignFirstResponder];
self.search = #0;
[self.tableView reloadData];
}
-(void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
{
self.searchController.searchBar.text = #"";
}
#pragma mark Table
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection: (NSInteger)section
{
return [self.arrayWithContent count];
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 120.0f;
}
- (PCRTweetFeedCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
PCRTweetFeedCell *cell = [tableView dequeueReusableCellWithIdentifier:#"PCRSavedTweetFeedCell"];
PCRTweet *tweetLocal = [[PCRTweet alloc] init];
if (cell == nil) {
cell = [[PCRTweetFeedCell alloc] initWithTweet:tweetLocal reuseIdentifier:#"PCRSavedTweetFeedCell"];
}
Tweet *tweetForIndex;
NSArray *arrayForCell = [[self.arrayWithContent reverseObjectEnumerator] allObjects];
NSArray *searchArrayForCell = [[self.searchResults reverseObjectEnumerator] allObjects];
if ([self.search isEqual:#0]){
tweetForIndex = [arrayForCell objectAtIndex:indexPath.row];
} else if ([self.search isEqual:#1]){
tweetForIndex = [searchArrayForCell objectAtIndex:indexPath.row];
}
NSString *date = [NSString stringWithFormat:#"%#", tweetForIndex.dateOfTweet];
TweetUser *tweetUser = tweetForIndex.whoPosted;
cell.t = tweetForIndex;
UIImage *imageOfTweetUser;
if (tweetUser.profilePicture) {
imageOfTweetUser = [UIImage imageWithData:tweetUser.profilePicture];
} else {
NSURL *urlWithProfilePicture = [NSURL URLWithString:tweetUser.profilePictureURL];
NSData *dataWithPic = [NSData dataWithContentsOfURL:urlWithProfilePicture];
imageOfTweetUser = [UIImage imageWithData:dataWithPic];
}
self.imageOfTweetUserGlobal = imageOfTweetUser;
cell.tweetMessage.text = tweetForIndex.messageOfTweet;
cell.tweetDate.text = date;
cell.tweetUserNameLabel.text = tweetForIndex.userNameOfTweetUser;
cell.profilePictureOfTwitterUserImageView.image = imageOfTweetUser;
cell.nameForPassing = [NSString stringWithFormat:#"%#'s Tweet", tweetUser.name];
return cell;
}
Well maybe this is your problem.
if ([self.search isEqual:#0]){
I think you are initially fetching the data but than once you init the searchController it starts looking for the searchResults.
The code you are looking is:
if ([self.search isActive]){
I found the solution. I changed
AppDelegate *del = [[AppDelegate alloc] init];
NSManagedObjectContext *context = del.managedObjectContext;
to
AppDelegate *del = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context =[del managedObjectContext];
and it works perfectly.
I am trying to find a way to make multiple annotation disclosure buttons open other views.
For example, annotation1 disclosure button needs to open ViewController 1.xib, annotation2 needs to open ViewController2.xib, and so on. Is this possible?
I've got this code so far, which sets the location according to coordinates, and sets the user location.
- (void)viewDidLoad
{
[super viewDidLoad];
_mapView.showsUserLocation = YES;
CLLocationCoordinate2D annotationCoord;
annotationCoord.latitude = 40.714353;
annotationCoord.longitude = -74.005973;
coord = 1;
MKPointAnnotation *annotationPoint = [[MKPointAnnotation alloc] init];
annotationPoint.coordinate = annotationCoord;
annotationPoint.title = #"New York";
[_mapView addAnnotation:annotationPoint];
CLLocationCoordinate2D annotationCoord1;
annotationCoord1.latitude = 51.511214;
annotationCoord1.longitude = -0.119824;
coord = 2;
MKPointAnnotation *annotationPoint1 = [[MKPointAnnotation alloc] init];
annotationPoint1.coordinate = annotationCoord1;
annotationPoint1.title = #"London";
[_mapView addAnnotation:annotationPoint1];
}
- (MKAnnotationView *)mapView:(MKMapView *)map viewForAnnotation:(id <MKAnnotation>)annotation
{
MKPinAnnotationView *mapPin = nil;
if(annotation != map.userLocation)
{
static NSString *defaultPinID = #"defaultPin";
mapPin = (MKPinAnnotationView *)[map dequeueReusableAnnotationViewWithIdentifier:defaultPinID];
if (mapPin == nil )
{
mapPin = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation
reuseIdentifier:defaultPinID] autorelease];
mapPin.canShowCallout = YES;
UIButton *infoButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
mapPin.rightCalloutAccessoryView = infoButton;
}
else
mapPin.annotation = annotation;
}
return mapPin;
}
What do I put into here:
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
// ???
}
All help is appreciated. Thanks
You could do something like this,
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
if ([view.annotation.title isEqualToString:#"The titel of your annotation"])
{
// Do something
}
}
I downloaded a dome about UIScorllView and UIPageControl. Why UIPageControl is not display?
Here is the code ,I am new in Iphone .Any help will be appreciated!
ScrollView.M I put the ResultViewController into ScrollView. I want scroll the resultViewController with PageController.
- (void)loadScrollViewWithPage:(int)page
{
if (page < 0)
return;
if (page >= pageNumber)
return;
ResultViewController *controller = [viewControllers objectAtIndex:page];
if ((NSNull *)controller == [NSNull null])
{
controller = [[ResultViewController alloc] initWithPageNumber:page locations:existLocations];
[viewControllers replaceObjectAtIndex:page withObject:controller];
[controller release];
}
if (controller.view.superview == nil)
{
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
controller.view.frame = frame;
[scrollView addSubview:controller.view];
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
existLocations = [FileManagerUtil readPlistFileForDictionary:#"Locations" fileName:#"CloudCheckLocations.plist"];
pageNumber = [existLocations count];
NSMutableArray *controllers = [[NSMutableArray alloc] init];
for (unsigned i = 0; i < pageNumber; i++)
{
[controllers addObject:[NSNull null]];
}
self.viewControllers = controllers;
[controllers release];
scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
[self.navigationItem setTitle:#"NetWork condition"];
scrollView.pagingEnabled = YES;
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * pageNumber, scrollView.frame.size.height);
scrollView.showsHorizontalScrollIndicator = NO;
scrollView.showsVerticalScrollIndicator = NO;
scrollView.scrollsToTop = NO;
scrollView.delegate = self;
pageContronl.numberOfPages = pageNumber;
pageContronl.currentPage = 0;
[pageContronl addTarget:self action:#selector(changePage:) forControlEvents:UIControlEventValueChanged];
[pageContronl setBackgroundColor:[UIColor blackColor]];
[self loadScrollViewWithPage:0];
[self loadScrollViewWithPage:1];
[scrollView addSubview:pageContronl];
[self.view addSubview:scrollView];
}
- (void)scrollViewDidScroll:(UIScrollView *)sender
{
if (pageControlUsed)
{
return;
}
CGFloat pageWidth = scrollView.frame.size.width;
int page = floor((scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
pageContronl.currentPage = page;
[self loadScrollViewWithPage:page - 1];
[self loadScrollViewWithPage:page];
[self loadScrollViewWithPage:page + 1];
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
pageControlUsed = NO;
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
pageControlUsed = NO;
}
- (IBAction)changePage:(id)sender
{
int page = pageContronl.currentPage;
[self loadScrollViewWithPage:page - 1];
[self loadScrollViewWithPage:page];
[self loadScrollViewWithPage:page + 1];
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
[scrollView scrollRectToVisible:frame animated:YES];
pageControlUsed = YES;
}
ScrollView.h
#interface ScrollViewController : UIViewController<UIScrollViewDelegate>
{
NSMutableArray *viewControllers;
NSString *currectNetWork;
NSString *flag;
NSString *locationName;
IBOutlet UIScrollView *scrollView;
IBOutlet UIPageControl *pageContronl;
BOOL pageControlUsed;
int pageNumber;
NSMutableDictionary *existLocations;
}
#property (nonatomic,retain) NSString *currectNetWork;
#property (nonatomic,retain) NSString *flag;
#property (nonatomic,retain) NSString *locationName;
#property (nonatomic,retain) UIPageControl * pageContronl;
#property (nonatomic,retain) UIScrollView * scrollView;
#property (nonatomic,retain) NSMutableArray *viewControllers;
#property (nonatomic,retain) NSMutableDictionary *existLocations;
(IBAction)changePage:(id)sender;
ResultViewControl.M
This method will call by ScrollView.M
- (id)initWithPageNumber:(int)page locations :(NSMutableDictionary *) locations
{
titilArray = [[NSArray alloc] initWithObjects:#"Today", #"Past 7 Day",#"Past 30 Day",nil];
if (self = [super initWithNibName:#"ResultViewController" bundle:nil])
{
pageNumber = page;
existLocations = locations;
}
return self;
}
Check the background colour of pageControl and parent view. If both have same colour (default white) page control will not display.
In IOS6, you have new methods pageIndicatorTintColor and currentPageIndicatorTintColor.
Hope this will help.
Check your top and bottom constraints of container view/ tableView(if present). Both must not be attached with Top/Bottom Layout Guide. Attach them with container Margins.
I made an App using cocos2D that has a hierarchical structure.
And I used a UIButton on glView in a child layer.
When I get back to parent layer, the button still on it's position.
I want to terminate that.
How can I do this?
Here's the code.
MainHallLayer.m
- (id)init
{
self = [super init];
if (self != nil)
{
CCMenu *menuLists = [CCMenu menuWithItems: nil];
menuLists.position = ccp(screenSize.width/2, screenSize.height/2);
[self addChild:menuLists z:10];
NSString *fontName = [NSString stringWithFormat:#"Chalkboard SE"];
CGFloat fontSize = 28;
{
CCLabelTTF *label = [CCLabelTTF labelWithString:#"Touch Field : Getting
touches coordinates" fontName:fontName fontSize:fontSize];
[label setColor:ccc3(0.0, 0.0, 0.0)];
label.anchorPoint = ccp(0, 0.5f);
CCMenuItem *menuItem = [CCMenuItemLabel itemWithLabel:label block:^(id
sender)
{
CCScene *scene = [TouchField1Layer node];
[ReturningNode returnToNodeWithParent:scene];
[[CCDirector sharedDirector] replaceScene:scene];
}];
[menuLists addChild:menuItem];
}
}
return self;
}
ReturnToNode.m
- (id)initWithParentNode:(CCNode *)parentNode
{
self = [super init];
if (self != nil)
{
CGSize screenSize = [[CCDirector sharedDirector]winSize];
CCLabelTTF *label = [CCLabelTTF labelWithString:#" <= Return"
fontName:#"Gill Sans"
fontSize:30.0f];
label.color = ccMAGENTA;
id tint_1 = [CCTintTo actionWithDuration:0.333f red:1.0 green:.0f blue:.0f];
id tint_2 = [CCTintTo actionWithDuration:0.333f red:.5f green:.5f blue:.0f];
id tint_3 = [CCTintTo actionWithDuration:0.333f red:.0f green:1.0 blue:.0f];
id tint_4 = [CCTintTo actionWithDuration:0.333f red:.0f green:.5f blue:.5f];
id tint_5 = [CCTintTo actionWithDuration:0.333f red:.0f green:.0f blue:1.0];
id tint_6 = [CCTintTo actionWithDuration:0.333f red:.5f green:.0f blue:.5f];
id sequence = [CCSequence actions:tint_1,tint_2,tint_3,tint_4,tint_5,tint_6, nil];
id repeatAction = [CCRepeatForever actionWithAction:sequence];
[label runAction:repeatAction];
CCLayerColor *labelBackground = [CCLayerColor layerWithColor:ccc4(0.0, 0.0, 70, 40) width:label.contentSize.width + 20 height:label.contentSize.height + 20];
[label addChild:labelBackground z:-1];
CCMenuItem *menuItem = [CCMenuItemLabel itemWithLabel:label block:^(id sender)
{
[self removeFromParentAndCleanup:YES];
[[CCDirector sharedDirector] replaceScene:[TouchControllerLayer scene]];
}];
CCMenu *menu = [CCMenu menuWithItems:menuItem, nil];
[menu alignItemsVertically];
menu.position = CGPointMake(label.contentSize.width/2 , screenSize.height - 20);
[self addChild:menu];
[parentNode addChild:self z:1000];
}
return self;
}
+ (id)returnToNodeWithParent:(CCNode *)parentNode
{
return [[[self alloc] initWithParentNode:parentNode] autorelease];
}
TouchField1Layer.m
- (id)init
{
self = [super init];
if (self != nil)
{
BackgroundLayer *background = [BackgroundLayer node];
TouchField3Layer *layer = [TouchField3Layer node];
[self addChild:background z:3];
[self addChild:layer z:2];
[self preparingTools];
}
return self;
}
- (void)preparingTools
{
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = CGRectMake(0, 0, 100, 40);
UIView *glView = [CCDirector sharedDirector].openGLView;
glView.tag = TAG_GLVIEW;
[glView addSubview:button];
}
Any advices, helps are welcome. Always thanks. Bless You.
If you add UIKit views you will have to remove them manually at the appropriate point in time. Cocos2D does not manage the lifetime of UIKit views, only classes derived from CCNode.
For example when changing scenes you will have to remove the UIButton from the glView either in -(void) dealloc or in -(void) cleanup.
This one has been doing my head in for months - So time to swallow my pride and reach out for a little help. At the moment this is being done in UIWebView as HTML5/JS controlled system. But UIWebview frankly sux and looking to make this last component native too.
I have a collection of videos and at specific timed points during the video, I am calling a page of instructions that relate to the timed period in the video. The video controls also act as a controller for the instructions pages. So whatever timed point is reached, the corresponding page is animated into place.
I've looked in many, many options, with the closest coming in with http video streaming and using timed metadata to initiate a view, but I am containing the videos locally on the device. And, as yet cannot find anything that looks like it will work. Seems simple enough in principle, but I'll be damned if I can find a decent solution...
Any ideas / pointers?
Here's the last attempt at going native with this before the remainder of my hair fell out - I think I may be seeing where I was heading in the wrong direction, but if you can spare a few moments, I'd really appreciate it!
OBJECTIVE is to have a shoutOut that lives below the video that contains a page of instructions. At x seconds, the content will be refreshed to correspond to that portion of the video and persist until the next shoutOut for fresh content. This I have managed to achieve successfully. Where I have been falling down (a lot) is when I scrub the video back to a previous section, the shoutOut content remains at the position from which I scrubbed and remains there permanently. Or as the code is below, simply doesn't re-apear as it is set to a timed visible duration.
Anyway, here's the code...
Header:
// START:import
#import <UIKit/UIKit.h>
// START_HIGHLIGHT
#import <MediaPlayer/MPMoviePlayerController.h>
#import "CommentView.h"
// END_HIGHLIGHT
// START:def
// START:wiring
#interface MoviePlayerViewController : UIViewController {
UIView *viewForMovie;
// END:wiring
// START_HIGHLIGHT
MPMoviePlayerController *player;
// END_HIGHLIGHT
// START:wiring
UILabel *onScreenDisplayLabel;
UIScrollView *myScrollView;
NSMutableArray *keyframeTimes;
NSArray *shoutOutTexts;
NSArray *shoutOutTimes;
}
#property (nonatomic, retain) IBOutlet UIView *viewForMovie;
// END:wiring
// START_HIGHLIGHT
#property (nonatomic, retain) MPMoviePlayerController *player;
// END_HIGHLIGHT
#property (nonatomic, retain) IBOutlet UILabel *onScreenDisplayLabel;
#property (nonatomic, retain) IBOutlet UIScrollView *myScrollView;
#property (nonatomic, retain) NSMutableArray *keyframeTimes;
// START_HIGHLIGHT
-(NSURL *)movieURL;
- (void)timerAction:(NSTimer*)theTimer;
- (void) playerThumbnailImageRequestDidFinish:(NSNotification*)notification;
- (void)handleTapFrom:(UITapGestureRecognizer *)recognizer;
- (IBAction) getInfo:(id)sender;
- (void)removeView:(NSTimer*)theTimer;
// END_HIGHLIGHT
// START:wiring
#end
// END:def
// END:wiring
// END:import
Main:
#implementation MoviePlayerViewController
// START:synth
#synthesize player;
#synthesize viewForMovie;
#synthesize onScreenDisplayLabel;
#synthesize myScrollView;
#synthesize keyframeTimes;
// END:synth
// Implement loadView to create a view hierarchy programmatically, without using a nib.
// START:viewDidLoad
// START:viewDidLoad1
- (void)viewDidLoad {
[super viewDidLoad];
keyframeTimes = [[NSMutableArray alloc] init];
shoutOutTexts = [[NSArray
arrayWithObjects:#"This is a test\nLabel at 2 secs ",
#"This is a test\nLabel at 325 secs",
nil] retain];
shoutOutTimes = [[NSArray
arrayWithObjects:[[NSNumber alloc] initWithInt: 2],
[[NSNumber alloc] initWithInt: 325],
nil] retain];
self.player = [[MPMoviePlayerController alloc] init];
self.player.contentURL = [self movieURL];
// END:viewDidLoad1
self.player.view.frame = self.viewForMovie.bounds;
self.player.view.autoresizingMask =
UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleHeight;
[self.viewForMovie addSubview:player.view];
[self.player play];
// START_HIGHLIGHT
[NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(timerAction:) userInfo:nil repeats:YES];
// END_HIGHLIGHT
// START:viewDidLoad1
[self.view addSubview:self.myScrollView];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(movieDurationAvailable:)
name:MPMovieDurationAvailableNotification
object:nil];
}
// END:viewDidLoad
// END:viewDidLoad1
// START:movieURL
-(NSURL *)movieURL
{
NSBundle *bundle = [NSBundle mainBundle];
NSString *moviePath =
[bundle
pathForResource:#"BigBuckBunny_640x360"
ofType:#"m4v"];
if (moviePath) {
return [NSURL fileURLWithPath:moviePath];
} else {
return nil;
}
}
// END:movieURL
int position = 0;
- (void)timerAction:(NSTimer*)theTimer {
NSLog(#"hi");
int count = [shoutOutTimes count];
NSLog(#"count is at %d", count);
if (position < count) {
NSNumber *timeObj = [shoutOutTimes objectAtIndex:position];
int time = [timeObj intValue];
NSLog(#"time is at %d", time);
if (self.player.currentPlaybackTime >= time) {
CommentView *cview = [[CommentView alloc]
initWithText:[shoutOutTexts objectAtIndex:position]];
[self.player.view addSubview:cview];
position++;
[NSTimer scheduledTimerWithTimeInterval:4.0f target:self selector:#selector(removeView:) userInfo:cview repeats:NO];
}
}
}
- (void)removeView:(NSTimer*)theTimer {
UIView *view = [theTimer userInfo];
[view removeFromSuperview];
}
/*
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
}
*/
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[super dealloc];
}
- (void) movieDurationAvailable:(NSNotification*)notification {
MPMoviePlayerController *moviePlayer = [notification object];
int duration = [moviePlayer duration];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(playerThumbnailImageRequestDidFinish:)
name:MPMoviePlayerThumbnailImageRequestDidFinishNotification
object:nil];
NSMutableArray *times = [[NSMutableArray alloc] init];
for(int i = 0; i < 20; i++) {
[times addObject:[NSNumber numberWithInt:5+i*((duration)/20)]];
}
[self.player requestThumbnailImagesAtTimes:times timeOption: MPMovieTimeOptionNearestKeyFrame];
}
int p = 0;
int ll=0;
- (void) playerThumbnailImageRequestDidFinish:(NSNotification*)notification {
NSDictionary *userInfo;
userInfo = [notification userInfo];
NSNumber *timecode;
timecode = [userInfo objectForKey: #"MPMoviePlayerThumbnailTimeKey"];
[keyframeTimes addObject: timecode];
UIImage *image;
image = [userInfo objectForKey: #"MPMoviePlayerThumbnailImageKey"];
int width = image.size.width;
int height = image.size.height;
float newwidth = 75 * ((float)width / (float)height);
self.myScrollView.contentSize = CGSizeMake((newwidth + 2) * 20, 75);
UIImageView *imgv = [[UIImageView alloc] initWithImage:image];
[imgv setUserInteractionEnabled:YES];
[imgv setFrame:CGRectMake(ll, 0, newwidth, 75.0f)];
ll+=newwidth + 2;
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(handleTapFrom:)];
[tapRecognizer setNumberOfTapsRequired:1];
[imgv addGestureRecognizer:tapRecognizer];
[tapRecognizer release];
[myScrollView addSubview:imgv];
}
- (void) getInfo:(id)sender
{
MPMovieMediaTypeMask mask = self.player.movieMediaTypes;
NSMutableString *mediaTypes = [[NSMutableString alloc] init];
if (mask == MPMovieMediaTypeMaskNone) {
[mediaTypes appendString:#"Unknown Media Type"];
} else {
if (mask & MPMovieMediaTypeMaskAudio) {
[mediaTypes appendString:#"Audio"];
}
if (mask & MPMovieMediaTypeMaskVideo) {
[mediaTypes appendString:#"Video"];
}
}
MPMovieSourceType type = self.player.movieSourceType;
NSMutableString *sourceType = [[NSMutableString alloc] initWithString:#""];
if (type == MPMovieSourceTypeUnknown) {
[sourceType appendString:#"Source Unknown"];
} else if (type == MPMovieSourceTypeFile) {
[sourceType appendString:#"File"];
} else if (type == MPMovieSourceTypeStreaming) {
[sourceType appendString:#"Streaming"];
}
CGSize size = self.player.naturalSize;
onScreenDisplayLabel.text = [NSString stringWithFormat:#"[Type: %#] [Source: %#] [Time: %.1f of %.f secs] [Playback: %.0fx] [Size: %.0fx%.0f]",
mediaTypes,
sourceType,
self.player.currentPlaybackTime,
self.player.duration,
self.player.currentPlaybackRate,
size.width,
size.height];
}
- (void)handleTapFrom:(UITapGestureRecognizer *)recognizer {
NSArray *subviews = [myScrollView subviews];
for (int i = 0; i < 20; i++) {
if (recognizer.view == [subviews objectAtIndex:i]) {
NSNumber *num = [keyframeTimes objectAtIndex:i];
self.player.currentPlaybackTime = [num intValue];
return;
}
}
}
#end
The Comment View Header:
#import <UIKit/UIKit.h>
#interface CommentView : UIView {
}
- (id)initWithFrame:(CGRect)frame andText:(NSString *) text;
- (id)initWithText:(NSString *) text;
#end
The Comment View Main:
#import "CommentView.h"
#implementation CommentView
- (id)initWithFrame:(CGRect)frame andText:(NSString *) text {
if ((self = [super initWithFrame:frame])) {
UIImage *image = [UIImage imageNamed:#"comment.png"];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
[self addSubview:imageView];
CGRect rect = CGRectMake(20, 20, 200.0f, 90.0f);
UILabel *label = [[UILabel alloc] initWithFrame:rect];
label.text = text;
label.numberOfLines = 3;
label.adjustsFontSizeToFitWidth = YES;
label.textAlignment = UITextAlignmentCenter;
label.backgroundColor = [UIColor clearColor];
[self addSubview:label];
}
return self;
}
- (id)initWithText:(NSString *) text {
if ((self = [super init])) {
UIImage *image = [UIImage imageNamed:#"comment.png"];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
[self addSubview:imageView];
CGRect rect = CGRectMake(20, 20, 200.0f, 90.0f);
UILabel *label = [[UILabel alloc] initWithFrame:rect];
label.text = text;
label.numberOfLines = 3;
label.adjustsFontSizeToFitWidth = YES;
label.textAlignment = UITextAlignmentCenter;
label.backgroundColor = [UIColor clearColor];
[self addSubview:label];
}
return self;
}
- (void)dealloc {
[super dealloc];
}
#end
Thoughts anyone?
Cheers!
What's wrong with monitoring currentPlaybackTime at regular intervals (assuming you are using an instance that implements MPMediaPlayback for playback).