I have a split view controller set up as a video player. The master view is a list of available videos and the detail view should play the selected video from the list.
I'm able to send the required properties to the detail view from the master (url, start time, end time...) but I'm only getting audio.
When a selection is made from the master view a delegate -(void)didselectVid: method gets fired in the detail view.
- (void)viewDidLoad
{
[super viewDidLoad];
if (selectedBool) {
timer = [NSTimer
scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(timedJob)
userInfo:nil
repeats:YES];
[timer fire];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(moviePlayerPlaybackStateDidChange:) name:MPMoviePlayerPlaybackStateDidChangeNotification object:nil];
NSString * videoStr = [NSString stringWithFormat:#"%#/%#", gameVideoURL , videoUrlStr];
NSString * urlStrEscaped = [videoStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL * dataURL = [NSURL URLWithString:urlStrEscaped];
moviePlayerCrl.movieSourceType = MPMovieSourceTypeStreaming;
moviePlayerCrl = [[MPMoviePlayerController alloc] initWithContentURL:dataURL];
[self registerForMovieNotifications];
}
}
- (void)registerForMovieNotifications
{
//movie is ready to play notifications
if ([self.moviePlayerCrl respondsToSelector:#selector(loadState)])
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(moviePlayerLoadStateChanged:) name:MPMoviePlayerLoadStateDidChangeNotification object:nil];
[self.moviePlayerCrl prepareToPlay];
}
//movie has finished notification
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(moviePlayBackDidFinish:) name:MPMoviePlayerPlaybackDidFinishNotification object:nil];
}
- (void)moviePlayerLoadStateChanged:(NSNotification*)notification
{
NSLog(#"load state changed");
//unless state is unknown, start playback
if ([self.moviePlayerCrl loadState] != MPMovieLoadStateUnknown)
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerLoadStateDidChangeNotification object:nil];
[self.moviePlayerCrl.view setFrame: CGRectMake(0,
[UIApplication sharedApplication].statusBarFrame.size.height
+ self.navigationController.navigationBar.frame.size.height
+ self.navigationController.toolbar.frame.size.height,
self.view.bounds.size.width,
self.view.bounds.size.height
- [UIApplication sharedApplication].statusBarFrame.size.height
- self.navigationController.navigationBar.frame.size.height
- self.navigationController.toolbar.frame.size.height
- self.tabBarController.tabBar.frame.size.height
)]; // x, y, width, height
[self.view addSubview:self.moviePlayerCrl.view];
[self.view bringSubviewToFront:self.moviePlayerCrl.view];
[self.moviePlayerCrl setFullscreen:NO animated:YES];
[self.moviePlayerCrl play];
[moviePlayerCrl setCurrentPlaybackTime:startPlaybackTime];
}
}
-(void)didselectVid:(VideoClipData *)vid
{
videoUrlStr = vid.evtUrlStr;
startTime = vid.evtStartStr;
endTime = vid.evtEndTimeStr;
gameNamesStr = vid.evtNameStr;
double startTimeInterval = [startTime doubleValue];
double endTimeInterval = [endTime doubleValue];
startPlaybackTime = startTimeInterval;
endPlaybackTime = endTimeInterval;
selectedBool = YES;
[self viewDidLoad];
}
The parent view frame and the MoviePlayerController view's frame have to be same before making MoviePlayerController.view a subview.
i.e Have one more view inside your master view say playerContainerView. Before adding the moviePlayerCrl.view as the subview of playerContainerView, set the playerContainerView and moviePlayerCrl.view to the same frame.
This should work.
I was able to fix this by calling didselectVid differently. With what I assume is proper split view delegation:
in master view .h :
#import "VideoClipData.h"
#protocol DetailDelegate <NSObject>
-(void)didselectEvtVid:(VideoClipData *) vid;
#end
#import <UIKit/UIKit.h>
#class DetailView;
#interface MasterView : UIViewController
#property (strong, nonatomic) DetailView *detailViewController;
#property (nonatomic, unsafe_unretained) id<DetailDelegate> delegate;
master view .m:
- (void)viewDidLoad {
[super viewDidLoad];
self.detailViewController = (DetailView *)[[self.splitViewController.viewControllers lastObject] topViewController];
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.detailViewController didselectEvtVid:gameVideos];
}
And instead of calling viewDidLoad again I called [self configureView]; as given in apple's split view example.
Related
I'm writing Plugin for AVPlayer in ios. I need to know when user click on Done button in AVPlayerViewController ( I want to know when user close video) and I don't access to AVPlayerViewController object. I checked event and found only rate property in AVPlayer set to 0 but in pause situation also the rate set to 0 . how I figure out these two situations ?
thanks all.
I met the problem when I was developing the player in windowed mode. It is currently impossible without tricks. Therefore, I used KVO to observe contentOverlayView which is actually a full-size view of AVPlayerViewController. Code is a bit complicated. In the example below, playerView property is a view from xib/storyboard on the view controller (see attached).
#import <AVKit/AVKit.h>
#import <AVFoundation/AVFoundation.h>
static NSString * const kBoundsProperty = #"bounds";
static void * kBoundsContext = &kBoundsContext;
#interface ViewController ()
#property (nonatomic, strong) AVPlayerViewController *playerViewController;
// View for windowed mode.
#property (weak, nonatomic) IBOutlet UIView *playerView;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.playerViewController = [[AVPlayerViewController alloc] init];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// Because in -viewDidLoad frame is unknown.
[self loadPlayerView];
}
- (void)loadPlayerView {
NSURL *videoURL = [NSURL URLWithString:#"https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"];
AVPlayer *player = [[AVPlayer alloc] initWithURL:videoURL];
self.playerViewController.player = player;
[player play];
[self addChildViewController:self.playerViewController];
[self.playerView addSubview:self.playerViewController.view];
// MARK: I would recommend to use constraints instead of frame.
self.playerViewController.view.frame = self.playerView.bounds;
[self.playerViewController.contentOverlayView addObserver:self forKeyPath:kBoundsProperty options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:kBoundsContext];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
if (context == kBoundsContext) {
CGRect oldBounds = [change[NSKeyValueChangeOldKey] CGRectValue];
CGRect newBounds = [change[NSKeyValueChangeNewKey] CGRectValue];
BOOL wasFullscreen = CGRectEqualToRect(oldBounds, [UIScreen mainScreen].bounds);
BOOL isFullscreen = CGRectEqualToRect(newBounds, [UIScreen mainScreen].bounds);
if (isFullscreen && !wasFullscreen) {
if (CGRectEqualToRect(oldBounds, CGRectMake(0.0, 0.0, newBounds.size.height, newBounds.size.width))) {
NSLog(#"Rotated fullscreen");
} else {
NSLog(#"Entered fullscreen");
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:#"DidEnterInFullscreen" object:nil];
});
}
} else if (!isFullscreen && wasFullscreen) {
NSLog(#"Exited fullscreen");
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:#"DidExitFromFullscreen" object:nil];
});
}
}
}
#end
I added a MPMoviePlayerController's view to my view of a viewcontroller, that i present on an iPad with UIModalPresentationFormSheet. This works fine so far, but when i tap the fullscreen button, the movie view changes to fullscreen but after the animation the modal viewcontroller appears above the view.
The only code I use is the following:
- (void)viewDidLoad
{
[super viewDidLoad];
self.player = [[MPMoviePlayerController alloc] initWithContentURL:[NSURL fileURLWithPath:url]];
[self.player prepareToPlay];
self.player.controlStyle = MPMovieControlStyleEmbedded;
self.player.view.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.player.view];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(willEnterFullScreen) name:MPMoviePlayerWillEnterFullscreenNotification object:self.player];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didEnterFullScreen) name:MPMoviePlayerDidEnterFullscreenNotification object:self.player];
}
- (void) updateViewConstraints
{
[super updateViewConstraints];
if (!self.addedContraints) {
[self.player.view autoPinEdgesToSuperviewEdgesWithInsets:UIEdgeInsetsZero];
self.addedContraints = YES;
}
}
This happens when I try to hide the viewcontrollers view.
Im using MPMoviePlayerController inside a class Ive built called MYVideo. Here is the code:
#import <MediaPlayer/MediaPlayer.h>
#import "MYVideo.h"
#interface MYVideo()
#property (strong, nonatomic) UIView * viewRef;
#property (strong, nonatomic) NSDictionary * contentData;
#property (strong, nonatomic) MPMoviePlayerController * videoController;
#end
#implementation MYVideo
#synthesize contentData,videoController,viewRef;
- (MYVideo*) initIntoView: (UIView*) view withContent:(NSDictionary*)contentDict{
self=[super init];
viewRef=view;
contentData = contentDict;
NSString *rawUrl = [[NSString alloc] initWithFormat:#"http://....com/app/%#.mp4", [contentDict objectForKey:#"cnid"]];
NSURL *videoUrl = [[NSURL alloc]initWithString:rawUrl];
videoController = [[MPMoviePlayerController alloc] initWithContentURL:videoUrl];
videoController.movieSourceType=MPMovieSourceTypeFile;
videoController.view.frame = viewRef.bounds;
[videoController.view setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
videoController.controlStyle=MPMovieControlStyleNone;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playbackFinished:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:videoController];
[viewRef addSubview:videoController.view];
return self;
}
- (void) playbackFinished: (NSNotification*) notification {
NSLog(#"playback finished");
if(videoController){
[videoController play];
}
}
- (void) play: (int) offset {
videoController.initialPlaybackTime=offset;
[videoController play];
}
- (void) stop {
[[NSNotificationCenter defaultCenter] removeObserver:self
name:#"playbackFinished"
object:nil];
if(videoController){
[videoController stop];
}
}
- (void) destroy {
if(videoController){
[videoController stop];
[videoController.view removeFromSuperview];
}
}
#end
My problem is that occasionally I get the following error:
playback finished
-[__NSCFString playbackFinished:]: unrecognized selector sent to instance 0x1664e6a0
Which I'm guessing is caused by the MPMoviePlayerController firing off the "playbackFinished" notification when this video class has already been released. Am I right in thinking this?
Thing is, this MYVideo class should still be there while the video is playing, this error only occurs when the video is playing and in the console log my NSLogging of "playback finished" immeditately preceeds the crash. Also I never shut down the class without first removing the "playbackFinished" observer.
Can anybody suggest to me why I would be getting this crash?
Many thanks.
Yeah it looks like you are not removing the observer, as this code:
[[NSNotificationCenter defaultCenter] removeObserver:self
name:#"playbackFinished"
object:nil];
should be:
[[NSNotificationCenter defaultCenter] removeObserver:self
name:#"playbackFinished:"
object:nil]; // ^
or better still (as you don't really care what's being called):
[[NSNotificationCenter defaultCenter] removeObserver:self
name:nil
object:videoController];
Also given you use tests like if (videoController) { ... } in so many places, you need to ensure it goes nil ASAP:
- (void)destroy {
if(videoController){
[videoController stop];
[videoController.view removeFromSuperview];
videoController = nil; // Add
}
}
I'm trying to create a simple modal view controller that lets you edit text using a text view. However, when I present the view controller modally, it slides in from the bottom left direction instead of just sliding in from the bottom.
Here's a video of the weird effect: http://youtu.be/9M_MHA5mt1M
My controller simply watches for the keyboard to show and then resizes the text view using auto layout appropriately. Here's the code:
#import "TextPicker.h"
#interface TextPicker ()
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *keyboardHeight;
#end
#implementation TextPicker
- (id)initWithText:(NSString *)text
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPhone" bundle:nil];
self = [storyboard instantiateViewControllerWithIdentifier:#"textPicker"];
if (self) {
self.text = text;
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self observeKeyboard];
//self.textView.text = self.text;
[self.textView becomeFirstResponder];
}
- (void) viewWillDisappear:(BOOL)animated {
[self.textView resignFirstResponder];
}
- (IBAction)savePressed:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
- (IBAction)cancelPressed:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (void) dealloc {
[self stopObervingKeyboard];
}
- (void)observeKeyboard {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillChangeFrameNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
- (void)stopObervingKeyboard {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillChangeFrameNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}
- (void)keyboardWillShow:(NSNotification *)notification {
NSDictionary *info = [notification userInfo];
NSValue *kbFrame = [info objectForKey:UIKeyboardFrameEndUserInfoKey];
CGRect keyboardFrame = [kbFrame CGRectValue];
self.keyboardHeight.constant = -keyboardFrame.size.height;
NSTimeInterval animationDuration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
[UIView animateWithDuration:animationDuration animations:^{
[self.view layoutIfNeeded];
}];
}
- (void)keyboardWillHide:(NSNotification *)notification {
NSDictionary *info = [notification userInfo];
NSTimeInterval animationDuration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
self.keyboardHeight.constant = 0;
[UIView animateWithDuration:animationDuration animations:^{
[self.view layoutIfNeeded];
}];
}
- (IBAction)dismissKeyboard:(id)sender {
[self.textView resignFirstResponder];
}
#end
Your view is animating as you have asked it to by wrapping the [self.view layoutIfNeeded] call inside an animation block.
In viewDidLoad you begin observing keyboard changes, and when you detect them you animate the adjustments, this is normally correct. But then, before the view does its first layout, you show the keyboard; this results in an animation for all the views from CGRectZero to their proper sizes. And this is the effect you are seeing.
So basically you need to give the view a chance to layout before your animated layoutIfNeeded call. Probably the easiest way to do this is simply to move [self.textView becomeFirstResponder]; to either viewWillAppear: or viewDidAppear:.
*As a side note, remember to call super in appearance calls. I noticed you did not call [super viewWillDisappear];.
I have a simple view controller (SecondViewController) used to manage a UITextview
(I'm building a simple editor)
this is the code of the SecondViewController.h
#interface SecondViewController : UIViewController {
IBOutlet UITextView *textView;
}
#property (nonatomic,retain) IBOutlet UITextView *textView;
#end
and this is the SecondViewController.m
//
// EditorViewController.m
// Editor
//
// Created by elio d'antoni on 13/01/11.
// Copyright 2011 none. All rights reserved.
//
#implementation SecondViewController
#synthesize textView;
/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView {
}
*/
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"uiViewBg.png"]];
textView.layer.borderWidth=1;
textView.layer.cornerRadius=5;
textView.layer.borderColor=[[UIColor darkGrayColor] CGColor];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillAppear:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillDisappear:) name:UIKeyboardWillHideNotification object:nil];
}
-(void) matchAnimationTo:(NSDictionary *) userInfo {
NSLog(#"match animation method");
[UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
[UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
}
-(CGFloat) keyboardEndingFrameHeight:(NSDictionary *) userInfo {
NSLog(#"keyboardEndingFrameHeight method");
CGRect keyboardEndingUncorrectedFrame =
[[ userInfo objectForKey:UIKeyboardFrameEndUserInfoKey ]
CGRectValue];
CGRect keyboardEndingFrame =
[self.view convertRect:keyboardEndingUncorrectedFrame
fromView:nil];
return keyboardEndingFrame.size.height;
}
-(CGRect) adjustFrameHeightBy:(CGFloat) change
multipliedBy:(NSInteger) direction {
NSLog(#"adjust method");
return CGRectMake(20,
57,
self.textView.frame.size.width,
self.textView.frame.size.height + change * direction);
}
-(void)keyboardWillAppear:(NSNotification *)notification {
NSLog(#"keyboard appear");
[UIView beginAnimations:nil context:NULL];
[self matchAnimationTo:[notification userInfo]];
self.textView.frame =
[self adjustFrameHeightBy:[self keyboardEndingFrameHeight:
[notification userInfo]]
multipliedBy:-1];
[UIView commitAnimations];
}
-(void)keyboardWillDisappear:(NSNotification *) notification {
NSLog(#"keyboard disappear");
[UIView beginAnimations:nil context:NULL];
[self matchAnimationTo:[notification userInfo]];
self.textView.frame =
[self adjustFrameHeightBy:[self keyboardEndingFrameHeight:
[notification userInfo]]
multipliedBy:1];
[UIView commitAnimations];
}
// 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];
}
#end
the problem is that if load the view controller from a tab bar controller the textView doesn't resize when the keyboard appear, but the SAME code works if loaded as a single view based app. I hope I was clear enough.
I used the tabBar template provided by xcode no modifications.
Your code looks right, did notice you are not releasing "textView" in dealloc and "viewDidUnload" but this should make any difference.
I'd be checking the notications are received and everthing is wired up ie textView is not nil