do you think is it possible add a code in an application (xcode) that allows print the screen of ipad, or send it to the mac
#import "AirPrintingViewController.h"
#implementation AirPrintingViewController
-(void)printItem {
NSString *path = [[NSBundle mainBundle] pathForResource:#"demo" ofType:#"png"];
NSData *dataFromPath = [NSData dataWithContentsOfFile:path];
UIPrintInteractionController *printController = [UIPrintInteractionController sharedPrintController];
if(printController && [UIPrintInteractionController canPrintData:dataFromPath]) {
printController.delegate = self;
UIPrintInfo *printInfo = [UIPrintInfo printInfo];
printInfo.outputType = UIPrintInfoOutputGeneral;
printInfo.jobName = [path lastPathComponent];
printInfo.duplex = UIPrintInfoDuplexLongEdge;
printController.printInfo = printInfo;
printController.showsPageRange = YES;
printController.printingItem = dataFromPath;
void (^completionHandler)(UIPrintInteractionController *, BOOL, NSError *) = ^(UIPrintInteractionController *printController, BOOL completed, NSError *error) {
if (!completed && error) {
NSLog(#"FAILED! due to error in domain %# with error code %u", error.domain, error.code);
}
};
[printController presentAnimated:YES completionHandler:completionHandler];
}
}
- (void)viewDidLoad {
[super viewDidLoad];
UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[btn addTarget:self action:#selector(printItem) forControlEvents:UIControlEventTouchDown];
[btn setTitle:#"PRINT" forState:UIControlStateNormal];
btn.frame = CGRectMake(0, 100, 320, 50);
[self.view addSubview:btn];
}
#end
,
with this code I can print a file in the path, how can I print my screen instead the file already in the path?
Sure, but only from your own apps. You cannot send screenshots of other apps.
Related
ALL,
On Windows cursors are indicated by the cur file extension. Is there something similar for OSX Cocoa application?
Is there a way in XCode to edit such files, just like in MSVC there is some basic graphical editor?
Also, is there a special API which will allow loading such files on Cocoa? Or I can just load them as images?
TIA!!
EDIT:
In the meantime I think I found a good solution that will satisfy my needs
NSString *path;
NSBundle *bundle = [NSBundle mainBundle];
NSString *path = [bundle pathForResource:cursor_file ofType:#"cur"];
if( path )
{
CGImageSourceRef image = CGImageSourceCreateWithURL( path, nil );
CFDictionaryRef properties =
CGImageSourceCopyPropertiesAtIndex( image, 0, nil );
NSInteger x = properties["hotspotX"];
NSInteger y = properties["hotspotY"];
}
Except that it doesn't compile:
I need to import the file where CGImageSourceCreateWithURL() is declared, and I need to fix the last 2 lines, because the array subscript can't be strings.
Could someone help, please?
The following source code will create a custom NSCursor in Xcode from a .cur image, after retrieving its “hotspot” coordinates from the file located in the app bundle Resource folder. Replace the contents of the main.m file with the code below and delete the pre-supplied AppDelegate files to avoid duplicate symbols. You will need to copy/paste the .cur file into the Xcode project folder and also drag and drop it into the Project navigator. Xcode will not automatically place this file into the Resources folder of your application bundle (unlike a .png file), so copy/paste it there as well after you have compiled the app. Reference: Hotspot in Windows cursor .cur loaded by NSImage?
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate> {
NSWindow *window;
CGFloat x,y;
}
#end
#implementation AppDelegate
- (void) customCursor {
// **** Get HotSpot from .cur file **** //
NSBundle *bundle = [NSBundle mainBundle];
NSURL *url = [bundle URLForImageResource:#"myCursor.cur"];
if( url ) {
CGImageSourceRef image = CGImageSourceCreateWithURL( (__bridge CFURLRef)url, nil );
NSDictionary *properties = (__bridge NSDictionary *)CGImageSourceCopyPropertiesAtIndex( image, 0, nil );
x = [[properties objectForKey:#"hotspotX"]floatValue];
y = [[properties objectForKey:#"hotspotY"]floatValue];
CFBridgingRelease(image);
CFBridgingRelease((__bridge CFTypeRef _Nullable)(properties));
}
NSCursor *customCursor = [[NSCursor alloc] initWithImage:[NSImage imageNamed:#"myCursor.cur"] hotSpot: NSMakePoint( x, y) ];
[customCursor set];
NSLog(#"x = %0.02f",x);
NSLog(#"y = %0.02f",y);
}
- (void) arrowCursor {
[[NSCursor arrowCursor] set];
}
- (void) buildMenu {
NSMenu *menubar = [NSMenu new];
[NSApp setMainMenu:menubar];
NSMenuItem *menuBarItem = [NSMenuItem new];
[menubar addItem:menuBarItem];
NSMenu *appMenu = [NSMenu new];
[menuBarItem setSubmenu:appMenu];
[appMenu addItemWithTitle:#"Quit" action:#selector(terminate:) keyEquivalent:#"q"];
}
- (void) buildWnd {
#define _wndW 500
#define _wndH 250
window = [[NSWindow alloc] initWithContentRect: NSMakeRect( 0, 0, _wndW, _wndH ) styleMask: NSWindowStyleMaskTitled | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskClosable backing: NSBackingStoreBuffered defer: NO];
[window center];
[window setTitle: #"Custom cursor"];
[window makeKeyAndOrderFront: nil];
// **** Button 1 **** //
NSButton *myBtn =[[NSButton alloc]initWithFrame:NSMakeRect( 30, 30, 135, 30 )];
[myBtn setBezelStyle:NSBezelStyleRounded ];
[myBtn setTitle: #"Custom Cursor"];
[myBtn setAction: #selector (customCursor)];
[[window contentView] addSubview: myBtn];
// **** Button 2 **** //
NSButton *myBtn2 =[[NSButton alloc]initWithFrame:NSMakeRect( 190, 30, 135, 30 )];
[myBtn2 setBezelStyle:NSBezelStyleRounded ];
[myBtn2 setTitle: #"Arrow Cursor"];
[myBtn2 setAction: #selector (arrowCursor)];
[[window contentView] addSubview: myBtn2];
// **** Quit btn **** //
NSButton *quitBtn = [[NSButton alloc]initWithFrame:NSMakeRect( _wndW - 50, 10, 40, 40 )];
[quitBtn setBezelStyle:NSBezelStyleCircular ];
[quitBtn setTitle: #"Q" ];
[quitBtn setAction:#selector(terminate:)];
[[window contentView] addSubview: quitBtn];
}
- (void) applicationWillFinishLaunching: (NSNotification *)notification {
[self buildMenu];
[self buildWnd];
}
#end
int main() {
NSApplication *application = [NSApplication sharedApplication];
AppDelegate *appDelegate = [[AppDelegate alloc] init];
[application setDelegate:appDelegate];
[application run];
return 0;
}
I am sorry for the long post but I am at my wits end and have been stumped for days over this. Here's the scenario. My app loads a response from core data, converts the values to NSStrings so that I can add them to an NSDictionary. Then the NSDictionary is converted to NSData so I can attach it as a file to email. The purpose of this is so I can create a database of information including images, videos, etc. I was able to get everything to work except I am having an issue with an NSMutableArray. Here's the process:
I create an event and then load the data for exporting with this code.
EventDB *per = [[EventDB alloc]init];
per.customLayoutArray = [record.customLayoutArray description] ?
[record.customLayoutArray description] : #"";
NSDictionary *dict = [per dictionaryWithValuesForKeys:#[#"customLayoutArray"];
NSData *data = [NSJSONSerialization dataWithJSONObject:dict options:0 error:NULL];
Then I email the data using MFMailComposer. Then I have a custom UTI that allows me the open the url from the email and then I import the data and load it into my coredata entity with this
if([[url pathExtension] isEqualToString:#"ipix"]) {
NSData *data = [NSData dataWithContentsOfURL:url];
NSError *error;
NSDictionary *jsonData = [NSJSONSerialization JSONObjectWithData:data
options:kNilOptions
error:&error];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"TSPItem"
inManagedObjectContext:self.managedObjectContext];
TSPItem *record = (TSPItem *)[[NSManagedObject alloc] initWithEntity:entity
insertIntoManagedObjectContext:self.managedObjectContext];
if (record) {
NSString *datetime = [jsonData objectForKey:#"customLayoutArray"];
record.customLayoutArray = [[datetime propertyList] mutableCopy];
}
That works fine. It does import the way I want but when I launch the event I get this crash message
** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason:
'-[__NSCFString apply]: unrecognized selector sent to instance 0x1c81a5f60
Now here's the code where it crashes.
NSMutableArray *archiveArray = self.record.customLayoutArray;
NSString *mycustom = [NSString stringWithFormat:#"%#_customlayout",
self.record.eventname];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:archiveArray];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:mycustom];
self.customLayoutArray = [NSKeyedUnarchiver unarchiveObjectWithData:data];
NSLog(#"BOOTH EVENT ID %#", self.customLayoutArray);
[self.customLayoutArray makeObjectsPerformSelector:#selector(apply)];
This is the log from BOOTH EVENT ID
BOOTH EVENT ID (
"<Rectangle:0x102d38cb0self.scaleValue=1.842393\n, self.rotateValue=0.000000\n, self.width=368.478516\n, self.height=368.478516\n, self.radius=0\n, self.frame={{104, 113.5}, {200, 200}}\n, self.isApplied=NO\n>",
"<Rectangle:0x102d393c0self.scaleValue=1.000000\n, self.rotateValue=0.000000\n, self.width=200.000000\n, self.height=200.000000\n, self.radius=0\n, self.frame={{253, 273.5}, {200, 200}}\n, self.isApplied=NO\n>"
)
The app crashes here. Now if I load the original event on my iPad (the one that I didn't export) the app works perfect and the NSLog response for BOOTH EVENT ID is identical.
The "apply" section refers to this file.
#import "Rectangle.h"
#import "DeviceSize.h"
#implementation Rectangle
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
self.clipsToBounds = YES;
self.userInteractionEnabled = YES;
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:[NSNumber numberWithFloat:self.scaleValue] forKey:#"ScaleValue"];
[coder encodeObject:[NSNumber numberWithFloat:self.rotateValue] forKey:#"RotateValue"];
[coder encodeObject:[NSNumber numberWithFloat:self.width] forKey:#"Width"];
[coder encodeObject:[NSNumber numberWithFloat:self.height] forKey:#"Height"];
[coder encodeObject:[NSNumber numberWithInteger:self.radius] forKey:#"Radius"];
[coder encodeObject:[NSNumber numberWithBool:self.isApplied] forKey:#"isApplied"];
[coder encodeObject:self.image forKey:#"Image"];
[coder encodeObject:self.backgroundColor forKey:#"BackgroundColor"];
[coder encodeObject:[NSValue valueWithCGPoint:self.center] forKey:#"CenterPoint"];
}
- (id)initWithCoder:(NSCoder *)coder {
self = [super init];
if (self) {
self.scaleValue = [[coder decodeObjectForKey:#"ScaleValue"] floatValue];
self.rotateValue = [[coder decodeObjectForKey:#"RotateValue"] floatValue];
self.width = [[coder decodeObjectForKey:#"Width"] floatValue];
self.height = [[coder decodeObjectForKey:#"Height"] floatValue];
self.radius = [[coder decodeObjectForKey:#"Radius"] integerValue];
self.isApplied = [[coder decodeObjectForKey:#"isApplied"] boolValue];
[self.layer setCornerRadius:self.radius];
self.image = [coder decodeObjectForKey:#"Image"];
[self setBackgroundColor:[coder decodeObjectForKey:#"BackgroundColor"]];
//
if (self.width == self.height)
{
CGRect rect = CGRectMake(0, 0,200, 200);
self.frame = rect;
}
if (self.width > self.height)
{
CGRect rect = CGRectMake(0, 0,200, 150);
self.frame = rect;
}
if (self.width < self.height)
{
CGRect rect = CGRectMake(0, 0,150, 200);
self.frame = rect;
}
self.center = [[coder decodeObjectForKey:#"CenterPoint"] CGPointValue];
}
return self;
}
- (void)drawRect:(CGRect)rect
{
/* Set UIView Border */
// Get the contextRef
CGContextRef contextRef = UIGraphicsGetCurrentContext();
// Set the border width
CGContextSetLineWidth(contextRef, 5.0);
// Set the border color to RED
CGContextSetRGBStrokeColor(contextRef, 255.0, 0.0, 0.0, 1.0);
// Draw the border along the view edge
CGContextStrokeRect(contextRef, rect);
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (NSString *)description {
NSMutableString *description = [NSMutableString stringWithFormat:#"<%#:%p", NSStringFromClass([self class]), self];
[description appendFormat:#"self.scaleValue=%f\n", self.scaleValue];
[description appendFormat:#", self.rotateValue=%f\n", self.rotateValue];
[description appendFormat:#", self.width=%f\n", self.width];
[description appendFormat:#", self.height=%f\n", self.height];
[description appendFormat:#", self.radius=%li\n", (long)self.radius];
[description appendFormat:#", self.frame=%#\n", NSStringFromCGRect(self.frame)];
[description appendFormat:#", self.isApplied=%#\n", self.isApplied ? #"YES" : #"NO"];
[description appendString:#">"];
return description;
}
- (id)copyWithZone:(NSZone *)zone {
Rectangle *copy = [[[self class] allocWithZone:zone] init];
if (copy != nil) {
copy.scaleValue = self.scaleValue;
copy.rotateValue = self.rotateValue;
copy.width = self.width;
copy.height = self.height;
copy.radius = self.radius;
copy.frame = self.frame;
copy.isApplied = self.isApplied;
}
return copy;
}
#end
#implementation Rectangle(ApplyRotate)
#pragma mark -
- (Rectangle *)apply {
if (self.isApplied) {
return self;
}
Rectangle *rectangle = self;
CGPoint centerPoint = rectangle.center;
CGAffineTransform rotate = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(rectangle.rotateValue));
CGAffineTransform scale = CGAffineTransformMakeScale(rectangle.scaleValue, rectangle.scaleValue);
CGAffineTransform scaleAndRotate = CGAffineTransformConcat(rotate, scale);
rectangle.transform = scaleAndRotate;
rectangle.center = centerPoint;
rectangle.isApplied = YES;
return rectangle;
}
#end
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,
I've got some functionality set up. But I'm lost on where to go from here. I can probably figure out what to do with facebook once I know how to actually use the images. I tried saving the image into NSDictionary and then redirecting to a different View Controller, but it won't let me redirect from within the imagePickerController method. So anybody have any idea how to use the image selected from camera roll?
My next idea was to save it in the NSDictionary and then just have a statement checking to see if the NSdictionary value changed but that's not an efficient way of doing it at all.
EDITED below to include the answer provided, but nothing happens, no image displays or anything. What am I missing?
- (void) useCameraRoll
{
if ([UIImagePickerController isSourceTypeAvailable:
UIImagePickerControllerSourceTypeSavedPhotosAlbum])
{
UIImagePickerController *imagePicker =
[[UIImagePickerController alloc] init];
imagePicker.delegate = self;
imagePicker.sourceType =
UIImagePickerControllerSourceTypePhotoLibrary;
imagePicker.mediaTypes = [NSArray arrayWithObjects:
(NSString *) kUTTypeImage,
nil];
imagePicker.allowsEditing = NO;
[self presentModalViewController:imagePicker animated:YES];
[imagePicker release];
newMedia = NO;
}
}
-(void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary *)info
{
NSString *mediaType = [info
objectForKey:UIImagePickerControllerMediaType];
[self dismissModalViewControllerAnimated:YES];
if ([mediaType isEqualToString:(NSString *)kUTTypeImage]) {
UIImage *image = [info
objectForKey:UIImagePickerControllerOriginalImage];
NSLog(#"image:%#",image);
displayPhoto.image = image;
[displayPhoto setFrame:CGRectMake(0, 0, 320, 480)];
[[self view] addSubview:displayPhoto];
}
else if ([mediaType isEqualToString:(NSString *)kUTTypeMovie])
{
// Code here to support video if enabled
}
}
Assuming you have called this from a view controller with a button that you have created, why not just add a UIImageView onto your view controller? Call it myPhotoImageView or something like that and then in the didFinishPickingMediaWithInfo method, just add the following line of code after
UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
as
myPhotoImageView.image = image;
To size the image view so that the aspect looks nice do this.
CGSize size = image.size;
CGRect photoFrame;
if (size.width > size.height)
{
// Landscape
CGFloat scaleFactor = 320 / size.width;
photoFrame = CGRectMake(0, 0, 320, size.height*scaleFactor);
}
else
{
// Portrait
CGFloat scaleFactor = 320 / size.height;
photoFrame = CGRectMake(0, 0, size.width*scaleFactor, 320);
}
myPhotoImageView.frame = photoFrame;
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).