Play youtube videos in fullscreen auto - objective-c

I am trying to play a youtube video using youtube embedded player in my ipad app. I want that as soon as the user clicks on the video thumbnail the video automatically should be loaded fullscreen on switching to landscape mode in ipad. Currently the user has to click the fullscreen button to play the video in fullscreen mode.
I want the same effect that is seen when we play the video using the default youtube app that comes with ipad.
This is the code that I am using to play you tube video on ipad.
embedHTML = #"<object width=\"640\" height=\"390\">
<param name=\"movie\"value=\"http://www.youtube.com/v/IYX_Ql-3U10&fs=1\">
<param name=\"allowFullScreen\" value=\"true\"></param>
<param name=\"allowScriptAccess\" value=\"always\"></param>
<embed id=\"yt\" src=\"http://www.youtube.com/v/youtube_video_id&fs=1\"
type=\"application/x-shockwave-flash\" position=\"fixed\"
allowfullscreen=\"true\" allowScriptAccess=\"always\"
width=\"640\" height=\"390\"></embed></object>";
And if this is not possible does anybody know if there is a reason or documentation supporting this decision.

You can't, it's not allowed :)

I was able to access this functionality from a subview titled FigPluginView within the youtube player's webview, using the iOS youtube helper iOS-youtube-player-helper.
FigPluginView class dump reference
Create a custom UIView class header that exposes the following method:
#import <Foundation/Foundation.h>
#interface WMFigPluginView : NSObject
-(void)scriptEnterFullScreen;
#end
Import the custom FigPluginView and YouTube player to the ViewController interface()
#import <youtube-ios-player-helper/YTPlayerView.h>
#import "WMFigPluginView.h"
#property(nonatomic, strong) YTPlayerView *playerView;
Use the following methods to init the youtube player, monitor state change, find and call the fullscreen script within the FigPluginView.
- (void)presentYoutubeVideo {
if ([self.view.subviews containsObject:_playerView]){
[_playerView removeFromSuperview];
}
CGRect playerFrame = CGRectMake(0, 0, 0, 0);
_playerView = [[YTPlayerView alloc]initWithFrame:playerFrame];
_playerView.delegate = self;
[_playerView.webView setAllowsInlineMediaPlayback: NO];
[self.view addSubview:_playerView];
[self.playerView loadWithVideoId:#"YouTubeVideoID" playerVars:#{#"showinfo":#0,#"modestbranding":#1,#"autoplay":#1,#"playsinline":#0}];
}
-(void)playerViewDidBecomeReady:(YTPlayerView *)playerView {
[_playerView playVideo];
}
-(void)playerView:(YTPlayerView *)playerView didChangeToState:(YTPlayerState)state {
//stop activity indicator
if (state == kYTPlayerStateBuffering) {
//start activity indicator
}
if (state == kYTPlayerStatePlaying) {
WMFigPluginView *figPluginView = (WMFigPluginView*)[self findFigPluginView:_playerView.webView];
[figPluginView scriptEnterFullScreen];
}
}
- (UIView *)findFigPluginView:(UIView *)view {
for (__unsafe_unretained UIView *subview in view.subviews) {
if ([NSStringFromClass(subview.class) hasSuffix:#"FigPluginView"]) {
return subview;
} else if (subview.subviews.count > 0) {
return [self findFigPluginView:subview];
}
}
return nil;
}
Hope this helps!

Looks like there is no straightforward solution. If my client will want it (I will know in a few days :-)) I will probably try to open new controller with webview that covers entire screen and then autoplay it like in this post:
How to make YouTube video starts to play automatically inside UIWebView

With respect to the authors of the other answers, it is completely possible to do this using UIWebViews in iOS AND without leveraging private APIs. Here's a pretty simple method to do so.
- (void)playYoutubeVideo:(NSString *)videoId {
UIWebView *webView = [[UIWebView alloc] initWithFrame:CGRectZero];
webView.center = self.view.center; //Technically not needed, I've found it looks better with this added (when the video is done playing it looks better while being minimized)
webView.mediaPlaybackRequiresUserAction = NO;
[self.view addSubview:webView];
NSString *youTubeVideoHTML = [NSString stringWithFormat:#"<html><head><style>body{margin:0px 0px 0px 0px;}</style></head> <body> <div id=\"player\"></div> <script> var tag = document.createElement('script'); tag.src = 'http://www.youtube.com/player_api'; var firstScriptTag = document.getElementsByTagName('script')[0]; firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); var player; function onYouTubePlayerAPIReady() { player = new YT.Player('player', { width:'768', height:'1024', videoId:'%#', events: { 'onReady': onPlayerReady } }); } function onPlayerReady(event) { event.target.playVideo(); } </script> </body> </html>", videoId];
[webView loadHTMLString:youTubeVideoHTML baseURL: [NSBundle mainBundle].bundleURL];
}
The crux of this implementation is using a UIWebView with a frame of CGRectZero together with custom HTML/Javascript that handles autoplaying the video. With the UIWebView with the CGRectZero frame, the user doesn't see any annoying intermediate page before the video is autoplayed, and the HTML/Javascript does the heavy lifting involved with getting the page to autoplay the video.
There's a slight delay before the video is presented. I haven't found a way to eliminate this couple second delay unfortunately.

Related

WebView: Blocked a frame with origin www.youtube.com

I get this message when I try to play youtube videos in my WebView
Blocked a frame with origin "https://www.youtube.com" from accessing a
frame with origin
"applewebdata://b102c2f1-19a4-4dea-bff2-b131be89929f". The frame
requesting access has a protocol of "https", the frame being accessed
has a protocol of "applewebdata". Protocols must match.
The baseUrl of my webview is currently set to nil.
I know I should change it to #"http://", but a lot of links and stuff in the view is based on it. If I change it I get a blank screen, because the html breaks.
So I was trying to intercept the call to youtube, when the user click on the youtube link, to change the baseUrl on the fly. But this seems to be not possible. For other actions I use this function and it works well:
- (void)webView:(WebView *)webView decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id < WebPolicyDecisionListener >)listener
How could I solve this issue? The videos were working correctly on previous OSX and I'm using the usual iFrame snippet to include the video.
<iframe class="youtube-player" type="text/html" width="{$width}" height="{$height}"
src="http://www.youtube.com/embed/{$youtubelink}?html5=1" frameborder="0">
UPDATE
I've to encode the url as workaround:
<iframe class="youtube-player" type="text/html" width="{$width}" height="{$height}" src="data:text/html;base64,aHR0cDovL3d3dy55b3V0dWJlLmNvbS9lbWJlZC97JHlvdXR1YmVsaW5rfT9odG1sNT0x" frameborder="0">
but what I get is the URL as string text in the html.
UPDATE 2
Using Google APIs.
<div id="youtubep" class="youtube-player"></div>
<script>
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
var player;
function onYouTubeIframeAPIReady() {
player = new YT.Player('youtubep', {
height: '390',
width: '640',
videoId: 'M7lc1UVf-VE',
events: {
'onReady': onPlayerReady
}
});
}
function onPlayerReady(event) {
event.target.playVideo();
}
</script>
Still it is not working:
[Warning] Untrusted origin: applewebdata://f53223b5-2587-4c0e-9937-bf3e9b07ff92 (www-embed-player.js, line 152, x26)
[Error] XMLHttpRequest cannot load https://redirector.googlevideo.com/videoplayback?mt=1449582813&mv=m&ms=au&source=youtube&key=yt6&clen=41094349&requiressl=yes&mm=31&mn=sn-5hnedn76&gcr=nl&initcwndbps=1451250&id=o-ALVwEvIylUeyOATePUVUFg-cbUR-qvn9AnZ9FdKuwcLH&upn=RA7SrQEDQdw&lmt=1432476385386698&ip=95.97.243.202&sparams=clen%2Cdur%2Cgcr%2Cgir%2Cid%2Cinitcwndbps%2Cip%2Cipbits%2Citag%2Clmt%2Cmime%2Cmm%2Cmn%2Cms%2Cmv%2Cnh%2Cpl%2Crequiressl%2Csource%2Cupn%2Cexpire&fexp=9407194%2C9408500%2C9408710%2C9415030%2C9416126%2C9417204%2C9417683%2C9418204%2C9419541%2C9420310%2C9420452%2C9422141%2C9422596%2C9422618%2C9423662%2C9424480&gir=yes&pl=18&dur=1343.599&sver=3&expire=1449604537&nh=IgpwcjA0LmFtczE2Kg4yMTMuNDYuMTgyLjEwNQ&mime=video%2Fmp4&itag=133&signature=4D2797CB2BE5FCFA7CB78FACD14E8423EC2013B7.329BD308AA58011A920BAEFACA0F5869C7B84FC1&ipbits=0&cpn=b5qfNKIpHmOvNtyU&alr=yes&keepalive=yes&ratebypass=yes&c=WEB&cver=html5&cmo=pf=1&range=0-3730&rn=12&playerretry=2&rbuf=0. Origin https://www.youtube.com is not allowed by Access-Control-Allow-Origin.
Basically no need of embedCode.
float width = 200.0f;
float height = 200.0f;
UIWebView *wv = [UIWebView new];
wv.frame = CGRectMake(60, 60, width, height);
wv.delegate = self;
// URL from safari address bar.
NSURL *url = [NSURL URLWithString:#"http://www.youtube.com/watch?v=fDXWW5vX-64"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[wv loadRequest:request];
[self.view addSubview:wv];
and add the UIWebViewDelegate protocol to the View controller.Hope it works.
Going the javascript route has worked well for me:
https://developers.google.com/youtube/iframe_api_reference?hl=en
I did have to add a base URL of "https://" to be able to interact with the callback events. Otherwise, they didn't appear to get fired.
Try replacing the iframe with the following:
<iframe class="youtube-player" type="text/html" width="{$width}" height="{$height}" src="data:text/html,<iframe src="http://www.youtube.com/embed/{$youtubelink}?html5=1" width=100% height=100% frameborder=0>" frameborder="0"></iframe>
Not 100% sure if this is the answer, but this is how I get videos to play with iframe. Maybe it will help you a bit:
NSString *website = model.contentVideoURL;
NSInteger height = self.view.frame.size.height - self.topView.frame.size.height - 70;
NSString *embedCode = [NSString stringWithFormat:#"<iframe width=\"100%%\" height=\"%li\" src=\"%#\" frameborder=\"0\" allowfullscreen></iframe>",(long)height, website];;
[[self webView] loadHTMLString:embedCode baseURL:nil];
Also try adding:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
To your info.Plist source code

UIWebView without Copy/Paste when displaying PDF files

I have tried to disable Copy/Paste in UIWebView by using a category and overriding canPerformAction and returning NO for copy, cut and paste selectors.
It worked as expected when I loaded a webpage or all other document formats (e.g. docx, pptx, rtf, txt) but not when I loaded a PDF document into the UIWebView.
It seems like there is some different mechanism that handles PDF documents in UIWebView which handles/responds to Copy selector, and therefore I can not block it.
I also tried to disable user interaction for all the subviews of the UIWebView's UIScrollView, which worked fine for other document formats except PDF.
Can anyone help figuring out how to disable Copy in UIWebView for PDF documents as well?
OK, so I've been experiencing the same problem myself and seem to find a solution, even if it's partial.
What I do is use a UILongPressGestureRecognizer to disable long press gestures that can lead to copy/paste.
The code:
UILongPressGestureRecognizer* longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(handleLongPress:)]; // allocating the UILongPressGestureRecognizer
longPress.allowableMovement=100; // Making sure the allowable movement isn't too narrow
longPress.minimumPressDuration=0.3; // This is important - the duration must be long enough to allow taps but not longer than the period in which the scroll view opens the magnifying glass
longPress.delegate=self; // initialization stuff
longPress.delaysTouchesBegan=YES;
longPress.delaysTouchesEnded=YES;
longPress.cancelsTouchesInView=YES; // That's when we tell the gesture recognizer to block the gestures we want
[webView addGestureRecognizer:longPress]; // Add the gesture recognizer to the view and scroll view then release
[[webView scrollView] addGestureRecognizer:longPress];
[longPress release];
This solution worked for me:
METHOD 1 - Detect Custom Long Presses
A) Create a subclass of UILongPressGestureRecogniser.
B) Include the canBePreventedByGestureRecognizer: method in your subclass, like this:
Header:
#import <UIKit/UIKit.h>
#interface CustomLongPress : UILongPressGestureRecognizer
#end
Implementation:
#import "CustomLongPress.h"
#implementation CustomLongPress
- (BOOL)canBePreventedByGestureRecognizer:(UIGestureRecognizer*)preventedGestureRecognizer {
return NO;
}
#end
That's the only code you need in the subclass.
C) Open up the view containing your uiwebview/pdf reader. Include your subclass: #import "CustomLongPress.h" and then add the custom UILongPressGestureRecogniser to your UIWebView, like this:
- (void)viewDidLoad
{
[super viewDidLoad];
//Load UIWebView etc
//Add your custom gesture recogniser
CustomLongPress * longPress = [[CustomLongPress alloc] initWithTarget:self action:#selector(longPressDetected)];
[pdfWebView addGestureRecognizer:longPress];
}
D) Detect the long press and switch your UIWebView's userInteraction Off then back On:
-(void)longPressDetected {
NSLog(#"long press detected");
[pdfWebView setUserInteractionEnabled:NO];
[pdfWebView setUserInteractionEnabled:YES];
}
Apparently the reason this works is because the UIWebView captures long presses with its own gesture recognisers, to the exclusion of any additional gesture recongisers you've added. But subclassing your gesture recognisers and preventing their exclusion by returning "NO" to the canBePreventedByGestureRecognizer: method overrides the default behaviour.
Once you can detect the long presses on PDFs, switching the userInteraction Off then On again prevents the UIWebView from actioning its default behaviour, i.e. launching a "Copy/Define" UIMenu or, if long pressing over a link, launching a pop-up actionsheet with "Copy" and "Open" actions.
METHOD 2 - Catch UIMenu NSNotification
Alternatively, if you just want to block the "Copy/Define" UIMenu, (but not affect long presses), you can add this line (listening for UIMenuControllerDidShowMenuNotification) to your ViewDidLoad:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(menuShown) name:UIMenuControllerDidShowMenuNotification object:nil];
and then add this method, using the same userInteraction Off/On method as above:
-(void)menuShown {
NSLog(#"menu shown");
[pdfWebView setUserInteractionEnabled:NO];
[pdfWebView setUserInteractionEnabled:YES];
}
First method taken from: https://devforums.apple.com/thread/72521?start=25&tstart=0, and second method from somewhere on Stack, sorry forgotten where. Please include if you know.
Great answer Zubaba. I’m using a webView to display colored and bolded text and I had the same problem. I put your solution into a method and call it just after I initialize the webView. I don’t seem to need the delegate.
self.textView = [[UIWebView alloc] initWithFrame:textFrame];
[self longPress:self.textView];
- (void)longPress:(UIView *)webView {
UILongPressGestureRecognizer* longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(handleLongPress)]; // allocating the UILongPressGestureRecognizer
longPress.allowableMovement=100; // Making sure the allowable movement isn't too narrow
longPress.minimumPressDuration=0.3; // This is important - the duration must be long enough to allow taps but not longer than the period in which the scroll view opens the magnifying glass
longPress.delaysTouchesBegan=YES;
longPress.delaysTouchesEnded=YES;
longPress.cancelsTouchesInView=YES; // That's when we tell the gesture recognizer to block the gestures we want
[webView addGestureRecognizer:longPress]; // Add the gesture recognizer to the view and scroll view then release
[webView addGestureRecognizer:longPress];
}
- (void)handleLongPress {
}
In case someone needs Zubaba's answer in Swift 3;
let longPress = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress))
longPress.allowableMovement = 100
longPress.minimumPressDuration = 0.3
longPress.delegate = self
longPress.delaysTouchesBegan = true
longPress.delaysTouchesEnded = true
longPress.cancelsTouchesInView = true
yourWebView.addGestureRecognizer(longPress)
yourWebView.scrollView.addGestureRecognizer(longPress)
func handleLongPress() {
// Show some alert to inform user or do nothing.
}
Here is a modification to Zubaba's answer in Swift 3 that ended up working for me to eliminate warning. I changed assignment longPress.delegate = self to longPress.delegate = self as? UIGestureRecognizerDelegate.
let longPress = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress))
longPress.allowableMovement = 100
longPress.minimumPressDuration = 0.3
longPress.delegate = self as? UIGestureRecognizerDelegate
longPress.delaysTouchesBegan = true
longPress.delaysTouchesEnded = true
longPress.cancelsTouchesInView = true
webView.addGestureRecognizer(longPress)
webView.scrollView.addGestureRecognizer(longPress)
webView.loadRequest(request)
The UILongPressGestureRecognizer is located in the UIPDFPageView. To get access to this view look at the view hierarchy in the Debug menu, currently you can access this view like so once you load the pdf to the web view:
let pdfPageView = myWebView.scrollview?.subviews[0]?.subviews[0]
Then to remove the Long Press use this method while passing in the pdfPageView:
func removeLongPressFromView(view: UIView){
if let gestures = view.gestureRecognizers{
for gesture in gestures{
if gesture is UILongPressGestureRecognzier{
view.removeGestureRecognizer(gesture)
}
}
}
}
Looking for a Xamarin.iOS solution.
var longPressGestureRecognizer = new CustomLongPressGestureRecognizer ((UILongPressGestureRecognizer obj) =>
{
Console.WriteLine ("CustomLongPressGestureRecognizer action");
});
webView.AddGestureRecognizer (longPressGestureRecognizer);
The approach given by Zubaba might look like this:
public class ZubabaLongPressGestureRecognizer : UIKit.UILongPressGestureRecognizer
{
public ZubabaLongPressGestureRecognizer (Action<UILongPressGestureRecognizer> action)
: base (action)
{
AllowableMovement = 100;
MinimumPressDuration = 0.3;
DelaysTouchesBegan = true;
DelaysTouchesEnded = true;
CancelsTouchesInView = true;
}
}
The open/copy/cancel menu still shows the first time a link is long held per PDF page. After that first long press, however, it properly does not show up for that page. That this is PDF page dependent does not give me confidence that there is a solution available.
Johnny Rockex's solutions might look like this:
public class RockexLongPressGestureRecognizer : UIKit.UILongPressGestureRecognizer
{
public RockexLongPressGestureRecognizer(UIKit.UIWebView webView, Action<UILongPressGestureRecognizer> action) :
base(UserInteractionAction(webView) + action)
{
}
private static Action<UILongPressGestureRecognizer> UserInteractionAction(UIKit.UIWebView webView)
{
return (UILongPressGestureRecognizer obj) =>
{
webView.UserInteractionEnabled = false;
webView.UserInteractionEnabled = true;
};
}
public override bool CanPreventGestureRecognizer(UIGestureRecognizer preventedGestureRecognizer)
{
return false;
}
}
and
notificationToken1 = UIMenuController.Notifications.ObserveMenuFrameDidChange (Callback);
notificationToken2 = NSNotificationCenter.DefaultCenter.AddObserver(UIMenuController.DidShowMenuNotification, Callback);
I was not able to get either to do anything. Helpfully someone else has done better with a Xamarin.iOS fix
1.ios11 iphone6 Object-C Without Copy/Paste/lookUp/share
2.
viewDidLoad{
.......
[self setupExclude];
}
- (void)setupExclude{
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPG)];
longPress.minimumPressDuration = 0.2;
[self.webview addGestureRecognizer:longPress];
UITapGestureRecognizer *singleTapGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:nil];
singleTapGesture.numberOfTapsRequired = 1;
singleTapGesture.numberOfTouchesRequired = 1;
[self.webview addGestureRecognizer:singleTapGesture];
UITapGestureRecognizer *doubleTapGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(longPG)];
doubleTapGesture.numberOfTapsRequired = 2;
doubleTapGesture.numberOfTouchesRequired = 1;
[self.webview addGestureRecognizer:doubleTapGesture];
[singleTapGesture requireGestureRecognizerToFail:doubleTapGesture];
}
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender{
BOOL res = [super canPerformAction:action withSender:sender];
UIMenuController.sharedMenuController.menuVisible = NO;
self.webview.userInteractionEnabled = NO;
self.webview.userInteractionEnabled = YES;
return res;
}
- (void)longPG{
UIMenuController.sharedMenuController.menuVisible = NO;
self.webview.userInteractionEnabled = NO;
self.webview.userInteractionEnabled = YES;
}
3. Done!

CocosDenshion music fade out

I'm using cocos denshion for the music in my game.
I'm currently playing background music with the code:
[[SimpleAudioEngine sharedEngine] playBackgroundMusic:#"backSong.mp3"];
However, when the game ends, I need the background music to fade out gradually. How can I fade out the background music, is there a way to do this? Thanks in advance!
Additionally, is ObjectAL any better than CocosDenshion? If so, what are the differences/advantages?
Try this:
float currentVolume = [SimpleAudioEngine sharedEngine].backgroundMusicVolume;
id fadeOut = [CCActionTween actionWithDuration:1 key:#"backgroundMusicVolume" from:currentVolume to:0.0f];
[[[CCDirector sharedDirector] actionManager] addAction:fadeOut target:[SimpleAudioEngine sharedEngine] paused:NO];
Hope that helps!
The only way i found of doing that is to schedule a method for execution and change the volume setting accordingly, kind of as follows:
-(void) fadeOutBackgroundMusic{
if (!currentBackgroundMusic_) {
CCLOG(#"GESoundServicesProviderImpl<fadeOutBackgroundMusic> : No background music at this time, ignoring.");
return;
}
fadeOutActionTickerCount_=0;
[self schedule:#selector(tickMusicFadeOut:)];
}
-(BOOL) isPlayingBackgroundMusic{
return isPlayingBackgroundMusic_;
}
#pragma mark sequencing stuff
-(void) tickMusicFadeOut:(ccTime) dt{
static float fadeTime;
static float volume;
static float maxVolume;
fadeOutActionTickerCount_++;
if (1==fadeOutActionTickerCount_) {
isPerformingFadeOutAction_ =YES;
fadeTime=0.0f;
volume=0.0f;
maxVolume=audioSettings_.masterVolumeGain*audioSettings_.musicCeilingGain;
} else {
fadeTime+=dt;
volume=MAX(0.0f, maxVolume*(1.0 - fadeTime/K_MUSIC_FADE_TIME));
[self setMusicVolume:volume];
if (fadeTime>K_MUSIC_FADE_TIME) {
volume=0.0f; // in case we have a .000000231 type seting at that moment.
}
if (volume==0.0f) {
CCLOG(#"GESoundServicesProviderImpl<tickMusicFadeOut> : background music faded out in %f seconds.",fadeTime);
[self setMusicVolume:0.0f];
[sharedAudioEngine_ stopBackgroundMusic];
self.currentBackgroundMusic=nil;
isPlayingBackgroundMusic_=NO;
isPerformingFadeOutAction_=NO;
[self unschedule:#selector(tickMusicFadeOut:)];
}
}
}
this is a simplified (edited) sample from my sound services provider implementation class (not tested as shown here). The general idea is to schedule yourself a method that will gradually fade out the music over a period of time (here an app-wide constant, K_MUSIC_FADE_TIME).

How to update UIWebView during an animation?

I've got a view that contains a UIWebView (With text images, etc) that is attached to a GestureRecognizer. Once the webView is "tapped," it flips over and displays updated text, images, etc. on this web view. I have the action associated with this recognizer as follows:
- (IBAction)flipWebViewGestureRecognizer:(id)sender
{
// isFrontView is boolean to determine if card is front or back
if(self.isFrontOfView)
{
self.isFrontOfView = NO;
[UIView transitionWithView: self.imageView
duration: 1.0
options: UIViewAnimationOptionTransitionFlipFromTop
animations: ^{ [self loadBack]; }
completion: nil];
}
else
{
self.isFrontOfView = YES;
[UIView transitionWithView: self.imageView
duration: 1.0
options: UIViewAnimationOptionTransitionFlipFromTop
animations: ^{ [self loadFront]; }
completion: nil];
}
}
The methods loadFront and loadBack are as follows (html itself is shortened to minimum):
- (void) loadFront
{
NSString *html = #"<p>Front Of Card</p>";
[self.viewWebView loadHTMLString:html baseURL:nil];
}
- (void) loadBack
{
NSString *html = #"<p>Front Of Card</p> <br /> <p>as well as back of card<p>";
[self.viewWebView loadHTMLString:html baseURL:nil];
}
From a functionality perspective this works great. The card can be flipped, and the web view is updated with the new HTML text. However, the update to the actual web view takes place after the card has already finished flipping (after the animation instead of during). What do I need to do to have the viewWebView already loaded with the updated html once the view animation finishes it's flip, rather than immediately afterwards.
I believe your problem is that UIWebView loads items asynchronously. If so, you need to start your load and then when the UIWebView calls the delegate function webViewDidFinishLoad: begin your animation.
However, it's possible that the UIWebView will render to the screen before the delegate method is called causing the content to flash up before the animation begins.
If so, then you may have to do this with two different UIWebViews and animate switching from one to the other.

iOS UIWebView inside a UIScrollView

I want to have a UIWebView inside a UIScrollView. The UIWebView will sometimes go out of bounds of the iPhone screen so I need a way to scroll the view so I can see all the content (but I don't want to use the built in scrolling of the UIWebView). So I'm thinking of putting all the content inside of a UIScrollView and then making the height of the UIScrollView to equal the height of the UIWebView and other views that are in it.
Here's an image to help describing my problem:
I did exactly the same thing. Here's how I made it work:
Add the UIWebView as a subview of the UIScrollView (obviously ;-)
Disable the native scrolling of the UIWebView (you can do that by iterating through the subviews of the UIWebView until you find it's UIScrollView and set scrollEnabled = NO on it.
Set the contentSize of your UIScrollView and the UIWebView's frame to the size of the UIWebViews HTML content.
The last point is bit tricky because you cannot be sure that the HTML is completely rendered when webViewDidFinishLoad: gets called on the UIWebViewDelegate.
Here's a reliable way to get the HTML content size:
1.Add a javascript function to the HTML that gets called when the HTML Document is ready:
window.onload = function() {
window.location.href = "ready://" + document.body.offsetHeight;
}
This functions sends a request that has the content height in it's URL.
2.In your UIWebViewDelegate you intercept this request:
- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType {
NSURL *url = [request URL];
if (navigationType == UIWebViewNavigationTypeOther) {
if ([[url scheme] isEqualToString:#"ready"]) {
float contentHeight = [[url host] floatValue];
yourScrollView.contentSize = CGSizeMake(yourScrollView.frame.size.width, contentHeight + yourWebView.frame.origin.y);
CGRect fr = yourWebView.frame;
fr.size = CGSizeMake(yourWebView.frame.size.width, contentHeight);
yourWebView.frame = fr;
return NO;
}
return YES;
}
Hope that helps
UPDATE
Here is the Swift 2 version:
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
guard navigationType == .Other else { return true }
if let url = request.URL, let host = url.host {
guard url.scheme == "ready" else { return true }
if let contentHeight = Float(host) {
yourScrollView.contentSize = CGSizeMake(yourScrollView.bounds.size.width, CGFloat(contentHeight))
var fr = webView.frame
fr.size = CGSizeMake(fr.size.width, CGFloat(contentHeight))
webView.frame = fr
}
return false
}
return true
}