I am working on an app that will let me play different videos on the iPad remotely with an iPhone. I have been following along with apples example for a video player but I've been having some troubles. The videos play just fine and I can get it to play from a variety of videos but switching between them a few times it will crash and i get this in the debugger:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'An AVPlayerItem cannot be associated with more than one instance of AVPlayer'
*** First throw call stack:
(0x380da8bf 0x37c261e5 0x30acbcb5 0x30abc1f7 0x30ac3bf3 0x30c93d55 0x30c95f7b 0x380ad2dd 0x380304dd 0x380303a5 0x37e07fcd 0x31bb0743 0x25e5 0x257c)
This is the code I am using to create the player:
MPMoviePlayerController *player = [[MPMoviePlayerController alloc] initWithContentOfURL:movieURL];
if (player) {
[self setMoviePlayerController:player];
[self installMovieNotificationObservers];
[player setContentURL:movieURL];
[player setMovieSourceType:sourceType];
[self applyUserSettingsToMoviePlayer];
[self.view addSubview:self.backgroundView];
[player.view setFrame:self.view.bounds];
[player.view setBackgroundColor = [UIColor blackColor];
[self.view addSubview:player.view];
}
And when the current movie is stopped I use:
[[self moviePlayerController] stop];
MPMoviePlayerController *player = [self moviePlayerController];
[player.view removeFromSuperview];
[self removeMovieNotificationHandlers];
[self setMoviePlayerController:nil];
Edit:
So Ive now discovered it happens every time i try and switch a video for the 11th time. weird! I'm practically pulling my hair out.
What fixed this problem for me was stopping the MPMoviePlayerController before doing the setContentURL.
MPMoviePlayerController *streamPlayer;
[streamPlayer stop];
[streamPlayer setContentURL:[NSURL URLWithString:selectedStation]];
In the implementation you have above, ARC doesn't know that the MPMoviePlayerController is finished and needs to be released.
Define MPMoviePlayerController in your .h file and make it accessible via a #property (and #synthesize).
#property (strong, nonatomic) MPMoviePlayerController * moviePlayerController;
Then take the result of your alloc & init and assign it to that. I.E.
self.moviePlayerController = [[MPMoviePlayerController alloc] initWithContentOfURL:movieURL];
you should just keep the moviePlayerController and if you want to play another video, just use
[self.moviePlayerController setContentURL:movieURL];
then in your notification callback:
- (void) moviePlayBackDidFinish:(NSNotification*)notification
{
self.moviePlayer = nil;
[self initanothermovieplayerandplay];
}
and please do not remove the notification handler from notification center, only do this in dealloc method of your VC.
now let's add some fade when the movie play is done:
- (void) moviePlayBackDidFinish:(NSNotification*)notification
{
[UIView animateWithDuration:1
delay: 0.0
options: UIViewAnimationOptionCurveEaseIn
animations:^{
// one second to fade out the view
self.moviePlayer.view.alpha = 0.0;
}
completion:^(BOOL finished){
self.moviePlayer = nil;
[self initanothermovieplayerandplay];
}
}
I had exactly the same problem.
Nothing was wrong with my and i guess with your code :)
Just a broken video file was mine problem.
Changing *.mov type to m4a for example fixed it. Maybe one or more of the files you play are corrupted?
Try to find out which files lead to crash and than if u can try to quickly forward backward the play position of one of them while playing - this should lead to crash in few tries.
This is how i found the bad files. By the way all my bad files were movies .mov made with Snapz Pro X :)
Not sure if it is the case here, but we had a lot of problems, because the MPMoviePlayer is a singleton somewhere under the hood.
What we did is, that we implemented our own MoviePlayer wrapper which can be used from UIView (actually we have exactly one subclass of UIView MoviePlayerView to show movies) and assures that only one instance of MPMoviePlayerController exists. The code goes like this (it contains some special stuff, we need to show previews/thumbs the way we want etc. you should clean up as well as some release-statements):
// MoviePlayer.h
#import <Foundation/Foundation.h>
#import <MediaPlayer/MediaPlayer.h>
#import "Logger.h"
#class MoviePlayerView;
#interface MoviePlayer : NSObject
{
#private
MPMoviePlayerController *controller;
MoviePlayerView *currentView;
}
#property (nonatomic, readonly) MPMoviePlayerController *controller;
+(MoviePlayer *) instance;
-(void) playMovie:(NSURL*)movieURL onView:(MoviePlayerView *)view;
-(void) stopMovie;
#end
// MoviePlayer.m
#import "MoviePlayer.h"
#import "MoviePlayerView.h"
#implementation MoviePlayer
#synthesize controller;
static MoviePlayer *player = nil;
#pragma mark Singleton management
+(MoviePlayer *) instance
{
#synchronized([MoviePlayer class])
{
if (player == nil)
{
player = [[super allocWithZone:NULL] init];
player->controller = [[MPMoviePlayerController alloc] init];
player->controller.shouldAutoplay = NO;
player->controller.scalingMode = MPMovieScalingModeAspectFit;
player->currentView = nil;
}
return player;
}
}
+(id) allocWithZone:(NSZone *)zone
{
return [[self instance] retain];
}
-(id) copyWithZone:(NSZone *)zone
{
return self;
}
-(id) retain
{
return self;
}
-(NSUInteger) retainCount
{
return NSUIntegerMax;
}
-(oneway void) release
{
// singleton will never be released
}
-(id) autorelease
{
return self;
}
#pragma mark MoviePlayer implementations
-(void) stopMovie
{
#synchronized(self)
{
if (controller.view.superview)
{
[controller.view removeFromSuperview];
}
if (controller.playbackState != MPMoviePlaybackStateStopped)
{
[controller pause];
[controller stop];
}
if (currentView)
{
NSNotificationCenter *ntfc = [NSNotificationCenter defaultCenter];
[ntfc removeObserver:currentView name:MPMoviePlayerLoadStateDidChangeNotification object:controller];
[ntfc removeObserver:currentView name:MPMoviePlayerPlaybackStateDidChangeNotification object:controller];
currentView = nil;
}
}
}
-(void) playMovie:(NSURL*)movieURL onView:(MoviePlayerView *)view
{
#synchronized(self)
{
[self stopMovie];
currentView = view;
NSNotificationCenter *ntfc = [NSNotificationCenter defaultCenter];
[ntfc addObserver:currentView
selector:#selector(loadStateDidChange:)
name:MPMoviePlayerLoadStateDidChangeNotification
object:controller];
[ntfc addObserver:currentView
selector:#selector(playbackStateDidChange:)
name:MPMoviePlayerPlaybackStateDidChangeNotification
object:controller];
[controller setContentURL:movieURL];
controller.view.frame = view.bounds;
[view addSubview: controller.view];
[controller play];
}
}
#end
// MoviePlayerView.h
#import <UIKit/UIKit.h>
#import "MoviePlayer.h"
#interface MoviePlayerView : MediaView
{
NSURL *movieURL;
NSURL *thumbnailURL;
UIImageView *previewImage;
UIView *iconView;
BOOL hasPreviewImage;
}
-(id) initWithFrame:(CGRect)frame thumbnailURL:(NSURL *)thumbnail movieURL:(NSURL *)movie;
-(void) loadStateDidChange:(NSNotification *)ntf;
-(void) playbackStateDidChange:(NSNotification *)ntf;
#end
// MoviePlayerView.m
#import "MoviePlayerView.h"
#interface MoviePlayerView()
-(void) initView;
-(void) initController;
-(void) playMovie;
-(void) setActivityIcon;
-(void) setMovieIcon:(float)alpha;
-(void) clearIcon;
-(CGPoint) centerPoint;
#end
#implementation MoviePlayerView
-(id) initWithFrame:(CGRect)frame thumbnailURL:(NSURL *)thumbnail movieURL:(NSURL *)movie
{
self = [super initWithFrame:frame];
if (self)
{
movieURL = [movie retain];
thumbnailURL = [thumbnail retain];
[self initView];
[self initController];
hasPreviewImage = NO;
loadingFinished = YES;
}
return self;
}
-(void) dealloc
{
[iconView release];
[previewImage release];
[movieURL release];
[super dealloc];
}
-(void)initView
{
self.backgroundColor = [UIColor blackColor];
// add preview image view and icon view
previewImage = [[UIImageView alloc] initWithFrame:self.bounds];
[previewImage setContentMode:UIViewContentModeScaleAspectFit];
UIImage *img = nil;
if (thumbnailURL)
{
img = [ImageUtils loadImageFromURL:thumbnailURL];
if (img)
{
previewImage.image = img;
hasPreviewImage = YES;
}
}
[self addSubview:previewImage];
[self setMovieIcon:(hasPreviewImage ? 0.8f : 0.3f)];
}
-(void)initController
{
UITapGestureRecognizer *rec = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(playMovie)];
[self addGestureRecognizer:rec];
[rec release];
}
-(void)playMovie
{
[[MoviePlayer instance] playMovie:movieURL onView:self];
[self setActivityIcon];
}
-(void) loadStateDidChange:(NSNotification *)ntf
{
MPMoviePlayerController *controller = [ntf object];
switch (controller.loadState)
{
case MPMovieLoadStatePlayable:
{
[self clearIcon];
[controller setFullscreen:YES animated:YES];
break;
}
case MPMovieLoadStateStalled:
{
[self setActivityIcon];
break;
}
default:
{
break; // nothing to be done
}
}
}
-(void) playbackStateDidChange:(NSNotification *)ntf
{
MPMoviePlayerController *controller = [ntf object];
switch (controller.playbackState)
{
case MPMoviePlaybackStatePlaying:
{
[self clearIcon];
break;
}
case MPMoviePlaybackStateStopped:
{
[self setMovieIcon:(hasPreviewImage ? 0.8f : 0.3f)];
break;
}
case MPMoviePlaybackStatePaused:
{
[self setMovieIcon:0.8f];
break;
}
case MPMoviePlaybackStateInterrupted:
{
[self setActivityIcon];
break;
}
default:
{
break; // nothing to be done
}
}
}
-(void) setActivityIcon
{
[self clearIcon];
iconView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
iconView.center = [self centerPoint];
[self addSubview:iconView];
[iconView performSelector:#selector(startAnimating)];
}
-(void) setMovieIcon:(float)alpha
{
[self clearIcon];
iconView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"icon_movie.png"]];
iconView.center = [self centerPoint];
iconView.alpha = alpha;
[self addSubview:iconView];
}
-(void) clearIcon
{
if (iconView)
{
SEL stop = #selector(stopAnimating);
if ([iconView respondsToSelector:stop])
{
[iconView performSelector:stop];
}
[iconView removeFromSuperview];
[iconView release];
iconView = nil;
}
}
-(CGPoint) centerPoint
{
return CGPointMake(roundf(self.bounds.size.width / 2.0f), roundf(self.bounds.size.height / 2.0f));
}
-(void)resize
{
for (UIView *view in [self subviews])
{
if (view == iconView)
{
iconView.center = [self centerPoint];
continue;
}
view.frame = self.bounds;
}
[self addCaptionLabel];
}
-(void) layoutSubviews
{
[super layoutSubviews];
[self resize];
}
#end
...
player = [[MPMoviePlayerController alloc] initWithContentURL: [NSURL URLWithString:...
...
but I didn't gave internet connection to phone (wi-fi) :)
I had the same problem. My solution is using prepareToPlay:
MPMoviePlayerController *player = [[MPMoviePlayerController alloc] initWithContentOfURL:movieURL];
if (player) {
[player prepareToPlay];
//...
}
This error seems to be thrown for lots of different reasons, but the reason I found was that the MPMoviePlayerController class freaks out if you call methods in a certain order. From an IRC Channel:
"apparently if you call prepareToPlay WHILE setting source type and
NOT setting the view yet causes this crash"
So I fixed this by just making sure that I called prepareToPlay: LAST (or second to last, with the last being play:).
It is also weird because my original code worked in iOS 5.1, but this problem suddenly manifested when I started using the iOS 6.0 sdk. It is possibly a bug in the MPMoviePlayerController code, so I'm going to be filing a radar report on it, as calling prepareToPlay: before setting the view / setting the sourceFileType should not throw an exception (or at least an exception that seemingly has nothing to do with the actual error)
Related
I tried to play video advertisements using the Custom Vast tag in the Google IMA integration application on macOS 12.0.1 and Xcode 13.1 but it gives the below error. I tried somany ways to but couldn't fix it. info.plist file I have shown below.
AdvancedExample[1476:21763] Error loading ads: Ads cannot be requested
because this AdsLoader failed to load
info.plist
VideoViewController.m
#import "VideoViewController.h"
#import AVFoundation;
#import "Constants.h"
typedef enum { PlayButton, PauseButton } PlayButtonType;
#interface VideoViewController () <AVPictureInPictureControllerDelegate, IMAAdsLoaderDelegate,
IMAAdsManagerDelegate, UIAlertViewDelegate>
// Tracking for play/pause.
#property(nonatomic) BOOL isAdPlayback;
// Play/Pause buttons.
#property(nonatomic, strong) UIImage *playBtnBG;
#property(nonatomic, strong) UIImage *pauseBtnBG;
// PiP objects.
#property(nonatomic, strong) AVPictureInPictureController *pictureInPictureController;
#property(nonatomic, strong) IMAPictureInPictureProxy *pictureInPictureProxy;
// Storage points for resizing between fullscreen and non-fullscreen
/// Frame for video player in fullscreen mode.
#property(nonatomic, assign) CGRect fullscreenVideoFrame;
/// Frame for video view in portrait mode.
#property(nonatomic, assign) CGRect portraitVideoViewFrame;
/// Frame for video player in portrait mode.
#property(nonatomic, assign) CGRect portraitVideoFrame;
/// Frame for controls in fullscreen mode.
#property(nonatomic, assign) CGRect fullscreenControlsFrame;
/// Frame for controls view in portrait mode.
#property(nonatomic, assign) CGRect portraitControlsViewFrame;
/// Frame for controls in portrait mode.
#property(nonatomic, assign) CGRect portraitControlsFrame;
/// Option for tracking fullscreen.
#property(nonatomic, assign) BOOL fullscreen;
/// Option for tracking load event
#property(nonatomic, assign) BOOL didRequestAds;
/// Gesture recognizer for tap on video.
#property(nonatomic, strong) UITapGestureRecognizer *videoTapRecognizer;
// IMA objects.
#property(nonatomic, strong) IMAAdsManager *adsManager;
#property(nonatomic, strong) IMACompanionAdSlot *companionSlot;
// Content player objects.
#property(nonatomic, strong) AVPlayer *contentPlayer;
#property(nonatomic, strong) AVPlayerLayer *contentPlayerLayer;
#property(nonatomic, strong) id playHeadObserver;
#end
#implementation VideoViewController
#pragma mark Set-up methods
// Set up the new view controller.
- (void)viewDidLoad {
[super viewDidLoad];
[self.topLabel setText:self.video.title];
// Set the play button image.
self.playBtnBG = [UIImage imageNamed:#"play.png"];
// Set the pause button image.
self.pauseBtnBG = [UIImage imageNamed:#"pause.png"];
self.isAdPlayback = NO;
self.fullscreen = NO;
self.didRequestAds = NO;
// Fix iPhone issue of log text starting in the middle of the UITextView
self.automaticallyAdjustsScrollViewInsets = NO;
// Set up CGRects for resizing the video and controls on rotate.
CGRect videoViewBounds = self.videoView.bounds;
self.portraitVideoViewFrame = self.videoView.frame;
self.portraitVideoFrame =
CGRectMake(0, 0, videoViewBounds.size.width, videoViewBounds.size.height);
CGRect videoControlsBounds = self.videoControls.bounds;
self.portraitControlsViewFrame = self.videoControls.frame;
self.portraitControlsFrame =
CGRectMake(0, 0, videoControlsBounds.size.width, videoControlsBounds.size.height);
// Set videoView on top of everything else (for fullscreen support).
[self.view bringSubviewToFront:self.videoView];
[self.view bringSubviewToFront:self.videoControls];
// Check orientation, set to fullscreen if we're in landscape
if ([[UIDevice currentDevice] orientation] == UIDeviceOrientationLandscapeLeft ||
[[UIDevice currentDevice] orientation] == UIDeviceOrientationLandscapeRight) {
[self viewDidEnterLandscape];
}
// Set up content player and IMA classes, then request ads. If the user selected "Custom",
// get the ad tag from the pop-up dialog.
[self setUpContentPlayer];
[self setUpIMA];
}
// Makes the request on first appearance only.
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if (self.didRequestAds) {
return;
}
self.didRequestAds = YES;
if ([self.video.tag isEqual:#"custom"]) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Tag"
message:#"Enter your test tag below"
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
alert.alertViewStyle = UIAlertViewStylePlainTextInput;
[alert show];
} else {
[self requestAdsWithTag:self.video.tag];
}
}
- (void)viewWillDisappear:(BOOL)animated {
[self.contentPlayer pause];
// Don't reset if we're presenting a modal view (for example, in-app clickthrough).
if ([self.navigationController.viewControllers indexOfObject:self] == NSNotFound) {
if (self.adsManager) {
[self.adsManager destroy];
self.adsManager = nil;
}
[self removeObservers];
self.contentPlayer = nil;
}
[super viewWillDisappear:animated];
}
//Remove ContentPlayer Observer
- (void)removeObservers {
if (self.playHeadObserver) {
[self.contentPlayer removeTimeObserver:self.playHeadObserver];
self.playHeadObserver = nil;
}
#try {
[self.contentPlayer removeObserver:self forKeyPath:#"rate"];
[self.contentPlayer removeObserver:self forKeyPath:#"currentItem.duration"];
} #catch (NSException *exception) { }
}
// If pop-up dialog was shown, request ads with provided tag on dialog close.
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
[self requestAdsWithTag:[[alertView textFieldAtIndex:0] text]];
}
// Initialize the content player and load content.
- (void)setUpContentPlayer {
// Load AVPlayer with path to our content.
NSURL *contentURL = [NSURL URLWithString:self.video.video];
self.contentPlayer = [AVPlayer playerWithURL:contentURL];
// Playhead observers for progress bar.
__weak VideoViewController *controller = self;
self.playHeadObserver = [controller.contentPlayer
addPeriodicTimeObserverForInterval:CMTimeMake(1, 30)
queue:NULL
usingBlock:^(CMTime time) {
CMTime duration = [controller
getPlayerItemDuration:controller.contentPlayer.currentItem];
[controller updatePlayHeadWithTime:time duration:duration];
}];
[self.contentPlayer addObserver:self forKeyPath:#"rate" options:0 context:#"contentPlayerRate"];
[self.contentPlayer addObserver:self
forKeyPath:#"currentItem.duration"
options:0
context:#"playerDuration"];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(contentDidFinishPlaying:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:self.contentPlayer.currentItem];
// Set up fullscreen tap listener to show controls.
self.videoTapRecognizer =
[[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(showFullscreenControls:)];
[self.videoView addGestureRecognizer:self.videoTapRecognizer];
// Create a player layer for the player.
self.contentPlayerLayer = [AVPlayerLayer playerLayerWithPlayer:self.contentPlayer];
// Size, position, and display the AVPlayer.
self.contentPlayerLayer.frame = self.videoView.layer.bounds;
[self.videoView.layer addSublayer:self.contentPlayerLayer];
// Set ourselves up for PiP.
self.pictureInPictureProxy =
[[IMAPictureInPictureProxy alloc] initWithAVPictureInPictureControllerDelegate:self];
self.pictureInPictureController =
[[AVPictureInPictureController alloc] initWithPlayerLayer:self.contentPlayerLayer];
self.pictureInPictureController.delegate = self.pictureInPictureProxy;
if (![AVPictureInPictureController isPictureInPictureSupported] && self.pictureInPictureButton) {
self.pictureInPictureButton.hidden = YES;
}
}
// Handler for keypath listener that is added for content playhead observer.
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context {
if (context == #"contentPlayerRate" && self.contentPlayer == object) {
[self updatePlayHeadState:(self.contentPlayer.rate != 0)];
} else if (context == #"playerDuration" && self.contentPlayer == object) {
[self
updatePlayHeadDurationWithTime:[self getPlayerItemDuration:self.contentPlayer.currentItem]];
}
}
#pragma mark UI handlers
// Handle clicks on play/pause button.
- (IBAction)onPlayPauseClicked:(id)sender {
if (!self.isAdPlayback) {
if (self.contentPlayer.rate == 0) {
[self.contentPlayer play];
} else {
[self.contentPlayer pause];
}
} else {
if (self.playHeadButton.tag == PlayButton) {
[self.adsManager resume];
[self setPlayButtonType:PauseButton];
} else {
[self.adsManager pause];
[self setPlayButtonType:PlayButton];
}
}
}
// Updates play button for provided playback state.
- (void)updatePlayHeadState:(BOOL)isPlaying {
[self setPlayButtonType:isPlaying ? PauseButton : PlayButton];
}
// Sets play button type.
- (void)setPlayButtonType:(PlayButtonType)buttonType {
self.playHeadButton.tag = buttonType;
[self.playHeadButton setImage:buttonType == PauseButton ? self.pauseBtnBG : self.playBtnBG
forState:UIControlStateNormal];
}
// Called when the user seeks.
- (IBAction)playHeadValueChanged:(id)sender {
if (![sender isKindOfClass:[UISlider class]]) {
return;
}
if (self.isAdPlayback == NO) {
UISlider *slider = (UISlider *)sender;
// If the playhead value changed by the user, skip to that point of the
// content is skippable.
[self.contentPlayer seekToTime:CMTimeMake(slider.value, 1)];
}
}
// Used to track progress of ads for progress bar.
- (void)adDidProgressToTime:(NSTimeInterval)mediaTime totalTime:(NSTimeInterval)totalTime {
CMTime time = CMTimeMakeWithSeconds(mediaTime, 1000);
CMTime duration = CMTimeMakeWithSeconds(totalTime, 1000);
[self updatePlayHeadWithTime:time duration:duration];
self.progressBar.maximumValue = totalTime;
}
// Get the duration value from the player item.
- (CMTime)getPlayerItemDuration:(AVPlayerItem *)item {
CMTime itemDuration = kCMTimeInvalid;
if ([item respondsToSelector:#selector(duration)]) {
itemDuration = item.duration;
} else {
if (item.asset && [item.asset respondsToSelector:#selector(duration)]) {
// Sometimes the test app hangs here for ios 4.2.
itemDuration = item.asset.duration;
}
}
return itemDuration;
}
// Updates progress bar for provided time and duration.
- (void)updatePlayHeadWithTime:(CMTime)time duration:(CMTime)duration {
if (CMTIME_IS_INVALID(time)) {
return;
}
Float64 currentTime = CMTimeGetSeconds(time);
if (isnan(currentTime)) {
return;
}
self.progressBar.value = currentTime;
self.playHeadTimeText.text =
[[NSString alloc] initWithFormat:#"%d:%02d", (int)currentTime / 60, (int)currentTime % 60];
[self updatePlayHeadDurationWithTime:duration];
}
// Update the current playhead duration.
- (void)updatePlayHeadDurationWithTime:(CMTime)duration {
if (CMTIME_IS_INVALID(duration)) {
return;
}
Float64 durationValue = CMTimeGetSeconds(duration);
if (isnan(durationValue)) {
return;
}
self.progressBar.maximumValue = durationValue;
self.durationTimeText.text = [[NSString alloc]
initWithFormat:#"%d:%02d", (int)durationValue / 60, (int)durationValue % 60];
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
switch (interfaceOrientation) {
case UIInterfaceOrientationLandscapeLeft:
case UIInterfaceOrientationLandscapeRight:
[self viewDidEnterPortrait];
break;
case UIInterfaceOrientationPortrait:
case UIInterfaceOrientationPortraitUpsideDown:
[self viewDidEnterLandscape];
break;
case UIInterfaceOrientationUnknown:
break;
}
}
- (void)viewDidEnterLandscape {
self.fullscreen = YES;
CGRect screenRect = [[UIScreen mainScreen] bounds];
if ([[[UIDevice currentDevice] systemVersion] floatValue] < 8.0) {
self.fullscreenVideoFrame = CGRectMake(0, 0, screenRect.size.height, screenRect.size.width);
self.fullscreenControlsFrame =
CGRectMake(0, (screenRect.size.width - self.videoControls.frame.size.height),
screenRect.size.height, self.videoControls.frame.size.height);
} else {
self.fullscreenVideoFrame = CGRectMake(0, 0, screenRect.size.width, screenRect.size.height);
self.fullscreenControlsFrame =
CGRectMake(0, (screenRect.size.height - self.videoControls.frame.size.height),
screenRect.size.width, self.videoControls.frame.size.height);
}
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationNone];
[[self navigationController] setNavigationBarHidden:YES];
self.videoView.frame = self.fullscreenVideoFrame;
self.contentPlayerLayer.frame = self.fullscreenVideoFrame;
self.videoControls.frame = self.fullscreenControlsFrame;
self.videoControls.hidden = YES;
}
- (void)viewDidEnterPortrait {
self.fullscreen = NO;
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationNone];
[[self navigationController] setNavigationBarHidden:NO];
self.videoView.frame = self.portraitVideoViewFrame;
self.contentPlayerLayer.frame = self.portraitVideoFrame;
self.videoControls.frame = self.portraitControlsViewFrame;
}
- (IBAction)videoControlsTouchStarted:(id)sender {
[NSObject cancelPreviousPerformRequestsWithTarget:self
selector:#selector(hideFullscreenControls)
object:self];
}
- (IBAction)videoControlsTouchEnded:(id)sender {
[self startHideControlsTimer];
}
- (void)showFullscreenControls:(UITapGestureRecognizer *)recognizer {
if (self.fullscreen) {
self.videoControls.hidden = NO;
self.videoControls.alpha = 0.9;
[self startHideControlsTimer];
}
}
- (void)startHideControlsTimer {
[self performSelector:#selector(hideFullscreenControls) withObject:self afterDelay:3];
}
- (void)hideFullscreenControls {
[UIView animateWithDuration:0.5
animations:^{
self.videoControls.alpha = 0.0;
}];
}
- (IBAction)onPipButtonClicked:(id)sender {
if ([self.pictureInPictureController isPictureInPictureActive]) {
[self.pictureInPictureController stopPictureInPicture];
} else {
[self.pictureInPictureController startPictureInPicture];
}
}
#pragma mark IMA SDK methods
// Initialize ad display container.
- (IMAAdDisplayContainer *)createAdDisplayContainer {
// Create our AdDisplayContainer. Initialize it with our videoView as the container. This
// will result in ads being displayed over our content video.
if (self.companionView) {
// MOE:strip_line [START ad_display_container_init]
return [[IMAAdDisplayContainer alloc] initWithAdContainer:self.videoView
viewController:self
companionSlots:#[ self.companionSlot ]];
// [END ad_display_container_init] MOE:strip_line
} else {
return [[IMAAdDisplayContainer alloc] initWithAdContainer:self.videoView
viewController:self
companionSlots:nil];
}
}
// Register companion slots.
- (void)setUpCompanions {
// MOE:strip_line [START companion_slot_declaration]
self.companionSlot =
[[IMACompanionAdSlot alloc] initWithView:self.companionView
width:self.companionView.frame.size.width
height:self.companionView.frame.size.height];
// [END companion_slot_declaration] MOE:strip_line
}
// Initialize AdsLoader.
- (void)setUpIMA {
if (self.adsManager) {
[self.adsManager destroy];
}
[self.adsLoader contentComplete];
self.adsLoader.delegate = self;
if (self.companionView) {
[self setUpCompanions];
}
}
// Request ads for provided tag.
- (void)requestAdsWithTag:(NSString *)adTagUrl {
[self logMessage:#"Requesting ads"];
// Create an ad request with our ad tag, display container, and optional user context.
IMAAdsRequest *request = [[IMAAdsRequest alloc]
initWithAdTagUrl:adTagUrl
adDisplayContainer:[self createAdDisplayContainer]
avPlayerVideoDisplay:[[IMAAVPlayerVideoDisplay alloc] initWithAVPlayer:self.contentPlayer]
pictureInPictureProxy:self.pictureInPictureProxy
userContext:nil];
[self.adsLoader requestAdsWithRequest:request];
}
// Notify IMA SDK when content is done for post-rolls.
- (void)contentDidFinishPlaying:(NSNotification *)notification {
// Make sure we don't call contentComplete as a result of an ad completing.
if (notification.object == self.contentPlayer.currentItem) {
[self.adsLoader contentComplete];
}
}
#pragma mark AdsLoader Delegates
- (void)adsLoader:(IMAAdsLoader *)loader adsLoadedWithData:(IMAAdsLoadedData *)adsLoadedData {
// Grab the instance of the IMAAdsManager and set ourselves as the delegate.
self.adsManager = adsLoadedData.adsManager;
self.adsManager.delegate = self;
// Create ads rendering settings to tell the SDK to use the in-app browser.
IMAAdsRenderingSettings *adsRenderingSettings = [[IMAAdsRenderingSettings alloc] init];
adsRenderingSettings.linkOpenerPresentingController = self;
// Initialize the ads manager.
[self.adsManager initializeWithAdsRenderingSettings:adsRenderingSettings];
}
- (void)adsLoader:(IMAAdsLoader *)loader failedWithErrorData:(IMAAdLoadingErrorData *)adErrorData {
// Something went wrong loading ads. Log the error and play the content.
[self logMessage:#"Error loading ads: %#", adErrorData.adError.message];
self.isAdPlayback = NO;
[self setPlayButtonType:PauseButton];
[self.contentPlayer play];
}
#pragma mark AdsManager Delegates
/*- (void)adsManager:(IMAAdsManager *)adsManager didReceiveAdEvent:(IMAAdEvent *)event {
[self logMessage:#"AdsManager event (%#).", event.typeString];
// When the SDK notified us that ads have been loaded, play them.
switch (event.type) {
case kIMAAdEvent_LOADED:
if (![self.pictureInPictureController isPictureInPictureActive]) {
[adsManager start];
}
break;
case kIMAAdEvent_PAUSE:
[self setPlayButtonType:PlayButton];
break;
case kIMAAdEvent_RESUME:
[self setPlayButtonType:PauseButton];
break;
case kIMAAdEvent_TAPPED:
[self showFullscreenControls:nil];
break;
default:
break;
}
}*/
- (void)adsManager:(IMAAdsManager *)adsManager didReceiveAdEvent:(IMAAdEvent *)event {
[self logMessage:#"AdsManager event (%#).", event.typeString];
switch (event.type) {
case kIMAAdEvent_LOADED:
[adsManager start];
break;
//case kIMAAdEvent_STARTED: {
break;
case kIMAAdEvent_PAUSE:
[self setPlayButtonType:PlayButton];
break;
case kIMAAdEvent_RESUME:
[self setPlayButtonType:PauseButton];
break;
case kIMAAdEvent_TAPPED:
[self showFullscreenControls:nil];
break;
default:
break;
}
}
- (void)adsManager:(IMAAdsManager *)adsManager didReceiveAdError:(IMAAdError *)error {
// Something went wrong with the ads manager after ads were loaded. Log the error and play the
// content.
[self logMessage:#"AdsManager error: %#", error.message];
self.isAdPlayback = NO;
[self setPlayButtonType:PauseButton];
[self.contentPlayer play];
}
- (void)adsManagerDidRequestContentPause:(IMAAdsManager *)adsManager {
// The SDK is going to play ads, so pause the content.
self.isAdPlayback = YES;
[self setPlayButtonType:PauseButton];
[self.contentPlayer pause];
}
- (void)adsManagerDidRequestContentResume:(IMAAdsManager *)adsManager {
// The SDK is done playing ads (at least for now), so resume the content.
self.isAdPlayback = NO;
[self setPlayButtonType:PauseButton];
[self.contentPlayer play];
}
#pragma mark Utility methods
- (void)logMessage:(NSString *)log, ... {
va_list args;
va_start(args, log);
NSString *s = [[NSString alloc] initWithFormat:[[NSString alloc] initWithFormat:#"%#\n", log]
arguments:args];
self.consoleView.text = [self.consoleView.text stringByAppendingString:s];
NSLog(#"%#", s);
va_end(args);
if (self.consoleView.text.length > 0) {
NSRange bottom = NSMakeRange(self.consoleView.text.length - 1, 1);
[self.consoleView scrollRangeToVisible:bottom];
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
I have created one application in Objective C. I want to implement functionality for showing selected content area of the screen on external display. For example, I have total 10 screens and I want to show 4 screens and don't want to show entire portion of the screen, just want to show selected view and area of the screen in external display.
I have done research on this, I found one tutorial, but that tutorial is available in swift language and I want to implement this things in objective c language.
The Swift tutorial in Objective C Code:
#import "ViewController.h"
#import "ExternalScreenViewController.h"
#interface ViewController ()
{
UIWindow *externalWindow;
UIWebView *webView;
}
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
webView = [[UIWebView alloc] init];
[self setupScreenNotifications];
NSURL *url = [NSURL URLWithString:#"http://www.spazstik-software.com"];
NSURLRequest *req = [NSURLRequest requestWithURL:url];
[webView loadRequest:req];
if ([[UIScreen screens] count] > 1) {
[self setupExternalScreen:[UIScreen screens][1]];
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)setupScreenNotifications {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(externalScreenDidConnect:) name:UIScreenDidConnectNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(externalScreenDidDisconnect:) name:UIScreenDidDisconnectNotification object:nil];
}
-(void)externalScreenDidConnect:(NSNotification *)notification {
UIScreen *screen = (UIScreen *)[notification object];
if (screen != nil) {
[self setupExternalScreen:screen];
}
}
-(void)externalScreenDidDisconnect:(NSNotification *)notification {
id obj = [notification object];
if (obj != nil) {
[self teardownExternalScreen];
}
}
-(void)setupExternalScreen:(UIScreen *)screen {
ExternalScreenViewController *vc = [[self storyboard] instantiateViewControllerWithIdentifier:#"ExternalScreen"];
externalWindow = [[UIWindow alloc]initWithFrame:screen.bounds];
externalWindow.rootViewController = vc;
externalWindow.screen = screen;
externalWindow.hidden = false;
}
-(void)teardownExternalScreen {
if (externalWindow != nil) {
externalWindow.hidden = true;
externalWindow = nil;
}
}
#end
For iOS 13 and up change the setupExternalScreen method to this
source : https://stackoverflow.com/a/61474635/9777391
-(void)setupExternalScreen:(UIScreen *)screen shouldRecurse:(bool)recurse {
UIWindowScene* matchingWindowScene = nil;
// For iOS13 and up find matching UIWindowScene
for(UIScene *aScene in UIApplication.sharedApplication.connectedScenes){
UIWindowScene *windowScene = (UIWindowScene*)aScene;
// Look for UIWindowScene that has matching screen
if (windowScene.screen == screen) {
matchingWindowScene = windowScene;
break;
}
}
if (matchingWindowScene == nil) {
// UIWindowScene has not been created by iOS rendered yet
// Lets recall self after delay of two seconds
if (recurse) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[self setupExternalScreen:screen shouldRecurse:NO];
});
}
// Dont proceed further if none found
return;
}
// Instantiate view controller
ExternalScreenViewController *vc = [[self storyboard] instantiateViewControllerWithIdentifier:#"ExternalScreen"];
// Setup external window
externalWindow = [[UIWindow alloc] initWithFrame:screen.bounds];
[externalWindow setRootViewController: vc];
// Set scene
[externalWindow setWindowScene:matchingWindowScene];
// Show the window.
[externalWindow setHidden:NO];
}
I have used AVPictureInPictureController in AVPPlayer and initialized it as AVPlayerLayer.
- (id)initWithFrame:(CGRect)frame contentURL:(NSURL*)contentURL
{
self = [super initWithFrame:frame];
if (self) {
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL:contentURL];
self.moviePlayer = [AVPlayer playerWithPlayerItem:playerItem];
playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.moviePlayer];
if ([AVPictureInPictureController isPictureInPictureSupported])
{
self.avVPictureInPictureController = [[AVPictureInPictureController alloc] initWithPlayerLayer:playerLayer];
}
[playerLayer setFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)];
[self.moviePlayer seekToTime:kCMTimeZero];
[self.layer addSublayer:playerLayer];
self.contentURL = contentURL;
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(playerFinishedPlaying) name:AVPlayerItemDidPlayToEndTimeNotification object:playerItem];
[self initializePlayer:frame];
}
return self;
}
Now when I try to present FPPopoverController ( a custom class to present PopOvers on iPhone and iPad ) it doesn't show on the screen.
I use the following code to present FPPopOverController
//That's how I call
self.audioSubtitlePopoverController.presentPopoverFromPoint(point)
//Definition of presentPopoverFromPoint
-(void)presentPopoverFromPoint:(CGPoint)fromPoint
{
self.origin = fromPoint;
//NO BORDER
if(self.border == NO)
{
_viewController.title = nil;
_viewController.view.clipsToBounds = YES;
}
_contentView.relativeOrigin = [_parentView convertPoint:fromPoint toView:_contentView];
[self.view removeFromSuperview];
NSArray *windows = [UIApplication sharedApplication].windows;
if(windows.count > 0)
{
_parentView=nil;
_window = [windows objectAtIndex:0];
//keep the first subview
if(_window.subviews.count > 0)
{
_parentView = [_window.subviews lastObject];
NSLog(#"%# %#",_parentView,_viewController);
[_parentView addSubview:self.view];
[_viewController viewDidAppear:YES];
[self.view bringSubviewToFront:_parentView];
}
}
else
{
[self dismissPopoverAnimated:NO];
}
[self setupView];
self.view.alpha = 0.0;
[UIView animateWithDuration:0.2 animations:^{
self.view.alpha = self.alpha;
}];
[[NSNotificationCenter defaultCenter] postNotificationName:#"FPNewPopoverPresented" object:self];
//navigation controller bar fix
if([_viewController isKindOfClass:[UINavigationController class]])
{
UINavigationController *nc = (UINavigationController*)_viewController;
UINavigationBar *b = nc.navigationBar;
CGRect bar_frame = b.frame;
bar_frame.origin.y = 0;
b.frame = bar_frame;
}
}
Even present from view doesn't work too
self.audioSubtitlePopoverController.presentPopoverFromView(self.view)
// Definition of presentPopoverFromView
-(void)presentPopoverFromView:(UIView*)fromView
{
SAFE_ARC_RELEASE(_fromView);
_fromView = SAFE_ARC_RETAIN(fromView);
[self presentPopoverFromPoint:[self originFromView:_fromView]];
}
Can anyone please assist. Would be grateful.
FPPopover is deprecated.
You should use the native Possibility to display Popovers or WEPopover instead.
In my app I've to create a custom alert view like the following:
So I followed this tutorial to create a custom alert view. I finished it but I'm getting issue in the following method:
- (void)addOrRemoveButtonWithTag:(int)tag andActionToPerform:(BOOL)shouldRemove {
NSMutableArray *items = [[NSMutableArray alloc]init];
[items addObject:self.buttonOk];
[items addObject:self.buttonClose];
int buttonIndex = (tag == 1);
if (shouldRemove) {
[items removeObjectAtIndex:buttonIndex];
} else {
if (tag == 1) {
[items insertObject:self.buttonOk atIndex:buttonIndex];
} else {
[items insertObject:self.buttonClose atIndex:buttonIndex];
}
}
}
I edited it than the tutorial because I don't need a UIToolBar for buttons. When I run the app it says me that I can't insert a nil object in an NSMutableArray, but I don't understand what's wrong, I hope you can help me to fix this issue.
UPDATE
Here's all the class code I developed:
#import "CustomAlertViewController.h"
#define ANIMATION_DURATION 0.25
#interface CustomAlertViewController ()
- (IBAction)buttonOk:(UIButton *)sender;
- (IBAction)buttonCancel:(UIButton *)sender;
#property (weak, nonatomic) IBOutlet UIButton *buttonClose;
#property (weak, nonatomic) IBOutlet UIButton *buttonOk;
#property (strong, nonatomic) IBOutlet UIView *viewAlert;
-(void)addOrRemoveButtonWithTag:(int)tag andActionToPerform:(BOOL)shouldRemove;
#end
#implementation CustomAlertViewController
- (id)init
{
self = [super init];
if (self) {
[self.viewAlert setFrame:CGRectMake(self.labelAlertView.frame.origin.x,
self.labelAlertView.frame.origin.y,
self.labelAlertView.frame.size.width,
self.viewAlert.frame.size.height)];
[self.buttonOk setTag:1];
[self.buttonClose setTag:0];
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)showCustomAlertInView:(UIView *)targetView withMessage:(NSString *)message {
CGFloat statusBarOffset;
if (![[UIApplication sharedApplication] isStatusBarHidden]) {
CGSize statusBarSize = [[UIApplication sharedApplication] statusBarFrame].size;
if (statusBarSize.width < statusBarSize.height) {
statusBarOffset = statusBarSize.width;
} else {
statusBarOffset = statusBarSize.height;
}
} else {
statusBarOffset = 0.0;
}
CGFloat width, height, offsetX, offsetY;
if ([[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationLandscapeLeft ||
[[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationLandscapeRight) {
width = targetView.frame.size.width;
height = targetView.frame.size.height;
offsetX = 0.0;
offsetY = -statusBarOffset;
}
[self.view setFrame:CGRectMake(targetView.frame.origin.x, targetView.frame.origin.y, width, height)];
[self.view setFrame:CGRectOffset(self.view.frame, offsetX, offsetY)];
[targetView addSubview:self.view];
[self.viewAlert setFrame:CGRectMake(0.0, -self.viewAlert.frame.size.height, self.viewAlert.frame.size.width, self.viewAlert.frame.size.height)];
[UIView beginAnimations:#"" context:nil];
[UIView setAnimationDuration:ANIMATION_DURATION];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
[self.viewAlert setFrame:CGRectMake(0.0, 0.0, self.viewAlert.frame.size.width, self.viewAlert.frame.size.height)];
[UIView commitAnimations];
[self.labelAlertView setText:#"CIAO"];
}
- (void)removeCustomAlertFromView {
[UIView beginAnimations:#"" context:nil];
[UIView setAnimationDuration:ANIMATION_DURATION];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
[self.viewAlert setFrame:CGRectMake(0.0, -self.viewAlert.frame.size.height, self.viewAlert.frame.size.width, self.viewAlert.frame.size.height)];
[UIView commitAnimations];
[self.view performSelector:#selector(removeFromSuperview) withObject:nil afterDelay:ANIMATION_DURATION];
}
- (void)removeCustomAlertFromViewInstantly {
[self.view removeFromSuperview];
}
- (BOOL)isOkayButtonRemoved {
if (self.buttonOk == nil) {
return YES;
} else {
return NO;
}
}
- (BOOL)isCancelButtonRemoved {
if (self.buttonClose == nil) {
return YES;
} else {
return NO;
}
}
- (void)removeOkayButton:(BOOL)shouldRemove {
if ([self isOkayButtonRemoved] != shouldRemove) {
[self addOrRemoveButtonWithTag:1 andActionToPerform:shouldRemove];
}
}
- (void)removeCancelButton:(BOOL)shouldRemove {
if ([self isCancelButtonRemoved] != shouldRemove) {
[self addOrRemoveButtonWithTag:0 andActionToPerform:shouldRemove];
}
}
- (void)addOrRemoveButtonWithTag:(int)tag andActionToPerform:(BOOL)shouldRemove {
NSMutableArray *items = [[NSMutableArray alloc]init];
[items addObject:self.buttonOk];
[items addObject:self.buttonClose];
int buttonIndex = (tag == 1);
if (shouldRemove) {
[items removeObjectAtIndex:buttonIndex];
} else {
if (tag == 1) {
[items insertObject:self.buttonOk atIndex:buttonIndex];
} else {
[items insertObject:self.buttonClose atIndex:buttonIndex];
}
}
}
- (IBAction)buttonOk:(UIButton *)sender {
[self.delegate customAlertOk];
}
- (IBAction)buttonCancel:(UIButton *)sender {
[self.delegate customAlertCancel];
}
#end
UPDATE 2
Code in which I use the CustomAlertView:
#import "PromotionsViewController.h"
#import "CustomAlertViewController.h"
#interface PromotionsViewController () <CustomAlertViewControllerDelegate> {
BOOL isDeletingItem;
}
#property(nonatomic,strong) CustomAlertViewController *customAlert;
- (IBAction)buttonBack:(UIButton *)sender;
#property (weak, nonatomic) IBOutlet UIButton *buttonAlert;
- (IBAction)buttonAlert:(UIButton *)sender;
#end
#implementation PromotionsViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self.buttonAlert setTitle:self.promotionSelected forState:UIControlStateNormal];
[self.customAlert setDelegate:self];
isDeletingItem = NO;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)buttonBack:(UIButton *)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
- (IBAction)buttonAlert:(UIButton *)sender {
self.customAlert = [[CustomAlertViewController alloc]init];
[self.customAlert removeOkayButton:NO];
[self.customAlert removeCancelButton:NO];
NSString *message = [NSString stringWithFormat:#"La tua offerta %# del 20%% è stata convertita in punti IoSi x10", self.promotionSelected];
[self.customAlert showCustomAlertInView:self.view withMessage:message];
isDeletingItem = YES;
}
- (void)customAlertOk {
if (isDeletingItem) {
[self.customAlert removeCustomAlertFromViewInstantly];
} else {
[self.customAlert removeCustomAlertFromView];
}
}
- (void)customAlertCancel {
[self.customAlert removeCustomAlertFromView];
if (isDeletingItem) {
isDeletingItem = NO;
}
}
#end
Maybe you're calling addOrRemoveButtonWithTag:andActionToPerform: at a time where your UI is not fully created, since UI elements are created asynchronously. So if you call this method, right after custom alert view instanciation, you'll get your crash because the buttons in the view are not created.
To solve this issue, you need to call addOrRemoveButtonWithTag:andActionToPerform: only once your custom alert has been added to the view hierarchy.
EDIT :
With the example code you gave in edit 2, you call these lines :
- (IBAction)buttonAlert:(UIButton *)sender {
self.customAlert = [[CustomAlertViewController alloc]init];
[self.customAlert removeOkayButton:NO];
[self.customAlert removeCancelButton:NO];
}
but when you have just instantiated CustomAlertViewController, its 2 buttons are not yet created, so I suggest you add 2 properties hasOkButton and hasCancelButton and a new constructor to your custom class like this one :
- (instancetype) initWithOk:(BOOL)OkButton AndCancel:(BOOL) CancelButton
{
if(self = [super init])
{
hasOkButton = OkButton;
hasCancelButton = CancelButton;
}
}
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// At this time, the custom UI buttons will be created in the UI view hierarchy
[self removeOkayButton: hasOkButton];
[self removeOkayButton: hasCancelButton];
}
And in the caller you can use the following to display a custom alert View:
- (IBAction)buttonAlert:(UIButton *)sender {
self.customAlert = [[CustomAlertViewController alloc] initWithOk:NO AndCancel:NO];
// ...
}
EDIT #2
I tried your solution in a real project, I made it work by using these lines int the caller :
- (IBAction)buttonAlert:(UIButton *)sender {
self.customAlert = [self.storyboard instantiateViewControllerWithIdentifier:#"customAlertView"];
self.customAlert.hasOK = NO;
self.customAlert.hasCancel = YES;
NSString *message = [NSString stringWithFormat:#"La tua offerta %# del 20%% è stata convertita in punti IoSi x10", self.promotionSelected];
[self.customAlert showCustomAlertInView:self.view withMessage:message];
isDeletingItem = YES;
}
In the CustomAlertViewController declare 2 visible properties hasOK and hasCancel in.h.
And modify your .m by adding method :
-(void)viewWillAppear:(BOOL)animated
{
[self removeOkayButton:self.hasOK];
[self removeCancelButton:self.hasCancel];
}
Be sure to modify your storyboard (if eligible) to have the "customAlertView" defined this way :
Don't forget also to bind your UIButton to the controller this can be a mistake too in your implementation :
Hope this will help you :)
I found on the web a tutorial to create custom alert view by using code, if you are interested you can go to this tutorial. I used it for my issue and it worked great! You have to fix a few things, because it uses deprecated command but it's easy to fix it.
If you are interested just take a look about this tutorial. I think you can integrate it in your app and after you can easily use for other stuff if it's necessary. I hope that my answer will help someone.
I want my whole project to run in portrait mode only and only the view that's for viewing the photo's can turn on landscape same as if using the facebook (we view photo's they turn landscape also but other view's remains in portrait) i want to know that how it's done as i have a table view that has images that i get from the DB from the server and every particular data contains different number's of photo's so now i want that after the user select's the photo's they should open up fully and can be viewed in landscape and in portrait as well i did tried
I am working with ios6
- (BOOL)shouldAutorotate
{
return NO;
}
implementing in all the view's so that they can remain in portrait but still they turn in landscape how should i restrict it and please can any one tell me that in one of my class i have images in table view(loaded from the DB) and i have another class that open's up the selected photo from the table
My PhotoView.m class
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *dic = [photos objectAtIndex:indexPath.row];
NSLog(#" *** url is %#",[dic objectForKey:#"url"]);
[self.delegate showPhotoAtUrl:[dic objectForKey:#"url"]];
}
My PhotoViewController.h class
#import <UIKit/UIKit.h>
#interface PhotoViewController : UIViewController <UIScrollViewDelegate>
{
NSString *url;
UIButton *doneButton;
}
#property (nonatomic, retain) UIImageView *imageView;
- (id) initWithPhotoUrl:(NSString *)photoUrl;
#end
and PhotoViewController.m class
#import "PhotoViewController.h"
#interface PhotoViewController ()
#end
#implementation PhotoViewController
#synthesize imageView;
- (id) initWithPhotoUrl:(NSString *)photoUrl
{
self = [super init];
if(self) {
url = [photoUrl retain];
}
return self;
}
- (void)dealloc
{
[url release];
self.imageView = nil;
[doneButton release];
[super dealloc];
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor blackColor];
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
spinner.tag = 1;
spinner.center = self.view.center;
[spinner startAnimating];
[self.view addSubview:spinner];
[spinner release];
[self performSelectorInBackground:#selector(loadPhotoData) withObject:nil];
doneButton = [[UIButton buttonWithType:UIButtonTypeRoundedRect] retain];
[doneButton addTarget:self action:#selector(doneTouched) forControlEvents:UIControlEventTouchUpInside];
[doneButton setTitle:#"done" forState:UIControlStateNormal];
[doneButton setBackgroundColor:[UIColor clearColor]];
doneButton.frame = CGRectMake(2, 2, 60, 30);
[self.view addSubview:doneButton];
}
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.title = #"";
self.navigationController.navigationBarHidden = YES;
}
- (void) doneTouched
{
[self.navigationController popViewControllerAnimated:YES];
}
- (void) loadComplete:(NSData *)imageData
{
if(!imageData) return;
UIActivityIndicatorView *spinner = (UIActivityIndicatorView *)[self.view viewWithTag:1];
if(spinner) {
[spinner stopAnimating];
[spinner removeFromSuperview];
}
UIImage *img = [UIImage imageWithData:imageData];
float scale = MIN(self.view.frame.size.width/img.size.width, self.view.frame.size.height/img.size.height);
self.imageView = [[[UIImageView alloc] initWithImage:img] autorelease];
self.imageView.frame = CGRectMake(0, 0, self.imageView.image.size.width*scale, self.imageView.image.size.height*scale);
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
self.imageView.center = scrollView.center;
scrollView.delegate = self;
scrollView.contentSize = self.imageView.frame.size;
scrollView.minimumZoomScale = 1;
scrollView.maximumZoomScale = 2;
[scrollView addSubview:self.imageView];
[self.view addSubview:scrollView];
[scrollView release];
[self.view bringSubviewToFront:doneButton];
}
- (UIView *) viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.imageView;
}
- (void) loadPhotoData
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]];
[self performSelectorOnMainThread:#selector(loadComplete:) withObject:imageData waitUntilDone:NO];
[pool drain];
}
#end
and in the main class i have have this method that calls the PhotoViewController to load the selected photo
- (void) showPhotoAtUrl:(NSString *)url
{
PhotoViewController *vc = [[PhotoViewController alloc] initWithPhotoUrl:url];
[self.navigationController pushViewController:vc animated:YES];
[vc release];
}
Now my problem is how should i get the images after getting selected from the table to be open up in the same type of the view that open's up in FB app (open's the selected photo in portrait/landscape compatible view) i am only able to get one photo in my PhotoViewController class can any one tell me how can i add all the photo's to the PhotoViewController class so that i get the effect that i want ?... Any code help will be very helpful .. Thanks in Advance for contributing your time
In Short i have two things to do :
I have to restrict my project to only portrait mode leaving only the photo's view to have landscape/portrait compatibility.
I am currently having the photo's in my table view that i want on selection to open up in the same style(landscape/portrait compatibile) as FB or other app's do while viewing the photo's.
These only work on iOS 6.0
-(BOOL)shouldAutorotate
{
return YES;
}
-(NSUInteger)supportedInterfaceOrientations
{
return (UIInterfaceOrientationLandscapeRight | UIInterfaceOrientationLandscapeLeft);
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationLandscapeRight;
}
The method will return which orientation mode to use first.
Below are supportedInterfaceOrientations:
UIInterfaceOrientationMaskPortrait
UIInterfaceOrientationMaskLandscapeLeft
UIInterfaceOrientationMaskLandscapeRight
UIInterfaceOrientationMaskPortraitUpsideDown
UIInterfaceOrientationMaskLandscape
UIInterfaceOrientationMaskAll
UIInterfaceOrientationMaskAllButUpsideDown
For iOS > 4.0
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return UIInterfaceOrientationIsLandscape(interfaceOrientation);
}
Other ViewControllers will have
-(BOOL)shouldAutorotate { return NO; }
-(BOOL)shouldAutorotateToInterfaceOrientation:
(UIInterfaceOrientation)interfaceOrientation{
return NO;
}