requestReview() deprecated in iOS 14, Alternative for xamarin.forms? - swift

I'm using
SKStoreReviewController.RequestReview ();
in a dependency service for Xamarin.Forms. However this is now deprecated from iOS 14. And I want to know how to integrate with the new
UIWindowScene
requestReview(in windowScene: UIWindowScene)
in Xamarin.Forms

First, you can try to add SKStoreReviewController.RequestReview(Window.WindowScene) in the FinishedLaunching method of your xxx.ios->AppDelegate.cs; start the project to see if it is correct.
If the above method goes wrong, using DependencyService is the right way to go.
Here is the interface code:
public interface MyInterface
{
void RequestReview();
}
Here is the implementation method of the interface in ios:
[assembly: Dependency(typeof(MyInterfaceImpl))]
namespace App19.iOS
{
public class MyInterfaceImpl : MyInterface
{
public void RequestReview()
{
var myv = UIDevice.CurrentDevice.CheckSystemVersion(14, 0);
if (myv)
{
UIWindow window = UIApplication.SharedApplication.Delegate.GetWindow();
SKStoreReviewController.RequestReview(window.WindowScene);
}
else
{
SKStoreReviewController.RequestReview();
}
}
}
}
You can call it in the OnStart method inside APP.xaml.cs:
protected override void OnStart()
{
if (Device.RuntimePlatform == Device.iOS)
{
DependencyService.Get<IReviewService>().RequestReview();
}
}

I might have a solution:
using StoreKit;
using UIKit;
public void RequestReview(){
var scene = UIApplication.SharedApplication.KeyWindow.WindowScene;
if (scene is null)
{
//handle the scene being null here
return;
}
SKStoreReviewController.RequestReview(scene);
}
I've found multiple ways of getting the current scene and they both worked when I tested them:
var scene = UIApplication.SharedApplication.KeyWindow.WindowScene;
var alsoScene = UIApplication.SharedApplication.Delegate.GetWindow()?.WindowScene;
You can read more about the difference between the two in this issue
The complete solution where you check the iOS version in order to call the right thing might look like this
using StoreKit;
using UIKit;
public void RequestReview()
{
var isIos14OrAbove = UIDevice.CurrentDevice.CheckSystemVersion(14, 0);
if (isIos14OrAbove)
{
var scene = UIApplication.SharedApplication.KeyWindow.WindowScene;
if (scene is null)
{
//handle the scene being null here
return;
}
SKStoreReviewController.RequestReview(scene);
}
else
{
SKStoreReviewController.RequestReview();
}
}
Hope this helps.

Related

How to play video in iPhone without fullscreen mode using WebView in Xamarin.Forms?

I am using WebView for playing video in my Xamarin.Forms project. On iPhone the video is playing in fullscreen, but I need to play the video within the WebView without going to fullscreen.
I have this issue only on the iPhone. On Android, Windows, and iPad devices the video is playing without fullscreen.
Case 1: Normal WebView
<WebView
x:Name="web_view"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"/>
web_view.Source = videoUrl;
Case 2: Custom webview
On some part, I am using a custom WebView. In this case the video is also being played in full-screen mode.
public class MyWebView : WebView
{
public static readonly BindableProperty UrlProperty = BindableProperty.Create(
propertyName: "Url",
returnType: typeof(string),
declaringType: typeof(MyWebView),
defaultValue: default(string));
public string Url
{
get { return (string)GetValue(UrlProperty); }
set { SetValue(UrlProperty, value); }
}
}
In the above 2 cases, I need to play the video without full-screen mode on the iPhone.
You could set AllowsInlineMediaPlayback as True in Custom Renderer
[assembly: ExportRenderer(typeof(MyWebView), typeof(MyWebViewRenderer))]
namespace CustomWebview.iOS
{
public class MyWebViewRenderer : ViewRenderer<MyWebView, WKWebView>
{
WKWebView wkWebView;
protected override void OnElementChanged(ElementChangedEventArgs<MyWebView> e)
{
base.OnElementChanged(e);
if (Control == null)
{
WKWebViewConfiguration configuration = new WKWebViewConfiguration();
configuration.AllowsInlineMediaPlayback = true;
wkWebView = new WKWebView(CGRect.Empty, configuration);
if (Element.Url != null)
{
wkWebView.LoadRequest(new NSUrlRequest(new NSUrl((Element).Url)));
}
SetNativeControl(wkWebView);
}
if (e.NewElement != null)
{
CallVideo();
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName.Equals(nameof(Element.Url)))
{
wkWebView.LoadRequest(new NSUrlRequest(new NSUrl((Element).Url)));
}
}
public override void Draw(CGRect rect)
{
base.Draw(rect);
Control.Frame = rect;
}
}
}
Added an if statement to exclude the null condition. And then load the URL when you set it in Forms. OnElementPropertyChanged will be called when the property of webview has been changed. So when you set its URL in forms, we could load requests in this method.

Mute button does not work when I switch the scenes

AudioManager and sound effects are working just fine at the beginning of scene, however it does not work when i switch the scenes. Or even with the same scene.
I would be grateful if you could help with that and PlayerPrefs issue. I have been searching all around the forum about PlayerPrefs but i could not be sure about what to type.
Thank you very much.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;
using System;
public class AudioManager : MonoBehaviour {
bool mutebutton = false;
public Sound[] sounds;
public static AudioManager instance;
// Use this for initialization
void Awake () {
if (instance == null)
instance = this;
else
{
Destroy(gameObject);
return;
}
DontDestroyOnLoad(gameObject);
foreach (Sound s in sounds)
{
s.source = gameObject.AddComponent<AudioSource>();
s.source.clip = s.clip;
s.source.volume = s.volume;
}
}
void Start()
{
Play("Theme");
PlayerPrefs.SetFloat("volume", AudioListener.volume);
PlayerPrefs.Save();
}
// Update is called once per frame
public void Play (string name)
{
Sound s = Array.Find(sounds, sound => sound.name == name);
s.source.Play();
}
public void Mute()
{
if (!mutebutton)
{
mutebutton = true;
AudioListener.volume = 0;
}
else
{
mutebutton = false;
AudioListener.volume = 1;
}
}
}
If you only miss "Theme" sound on scene load:
Start() method won't be called again on scene load.
You have to actively call Play("Theme") after loading a scene.

Reload GameScene without reloading UIScene in Unity3d

I have 2 scenes "GamePlay" and "GameUI".
I load GameUI additively with GamePlay
void Awake () {
if (!instance) {
instance = this;
} else {
Destroy(this.gameObject) ;
}
SceneManager.LoadScene ("GameUI", LoadSceneMode.Additive);
DontDestroyOnLoad(this.gameObject);
}
Now when I reload the "GamePlay" scene using
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
How can I prevent reloading "GameUI" scene?
You could add an MonoBehaviour that is attached to the GameUI which contains a static bool that indicates if the object exists. Make your whole UI Scene a child of one object with the following script.
public class UIExistance : MonoBehaviour
{
public static bool Exists { get; private set; }
void Awake()
{
DontDestroyOnLoad(transform.gameObject);
UIExistance.Exists = true;
}
void OnDestroy()
{
UIExistance.Exists = false;
}
}
When you are in your posted Awake method you can check with if(!UIExistance.Exists) whether the UI is already in the scene.

MovieTexture Never Plays In Build

I have written a script to play OGG files as a MovieTexture, i've tried both embedded asset files and also from the web. The problem I'm having is that the videos play in debug move (When I hit the play button and test in the "Game" tab) but they never work when I build to an executable..
using UnityEngine;
namespace Assets.Scripts
{
[RequireComponent(typeof(AudioSource))]
public class VideoScreen : MonoBehaviour
{
public string videoUrl;
public bool autoPlay = true;
public bool loop = true;
public bool playAudio = true;
public float opacity = 1.0f;
private bool hasLoaded;
private MovieTexture movieTexture;
public void Start ()
{
if (string.IsNullOrEmpty(videoUrl))
{
return;
}
var data = new WWW(videoUrl);
movieTexture = data.movie as MovieTexture;
}
public void Update()
{
if (movieTexture.isReadyToPlay && !hasLoaded)
{
renderer.material = new Material(Shader.Find("Custom/Unlit Transparent Color")) { mainTexture = movieTexture };
audio.clip = movieTexture.audioClip;
SetLoop(loop);
if (autoPlay)
{
Play();
}
hasLoaded = true;
}
var textureColor = renderer.material.color;
textureColor.a = opacity;
renderer.material.color = textureColor;
}
private void Play()
{
movieTexture.Stop();
movieTexture.Play();
if (playAudio)
{
audio.Stop();
audio.Play();
}
}
private void Pause()
{
movieTexture.Pause();
if (playAudio)
{
audio.Pause();
}
}
private void Stop()
{
movieTexture.Stop();
if (playAudio)
{
audio.Stop();
}
}
private void SetLoop(bool loopStatus)
{
movieTexture.loop = loopStatus;
if (playAudio)
{
audio.loop = loopStatus;
}
}
}
}
Can anyone shed any light on this behaviour?
Thanks
Turns out the custom Transparent Diffuse shader I was using was causing the issue! Doh!!
renderer.material = new Material(Shader.Find("Custom/Unlit Transparent Color")) { mainTexture = movieTexture };
now becomes
renderer.material.mainTexture = movieTexture;

monotouch dispose UIWebView

Using a UIWebView in my application and I cant seem to release the memory it's using.
I have a UIButton which loads a view controller which holds the UIWebView.
When the webView is loaded the Real Memory (checking it using Instruments) rises but doesn't get lower after my attempts to release it.
When the button is pressed:
if (childBroswer != null)
{
childBroswer.Dispose();
childBroswer = null;
}
childBroswer = new ChildBrowser(url);
AppDelegate d = (AppDelegate)UIApplication.SharedApplication.Delegate;
if ( d.rootNavigationController.RespondsToSelector(
new MonoTouch.ObjCRuntime.Selector("presentViewController:animated:completion:")))
{
d.rootNavigationController.PresentViewController(childBroswer, true, null);
}
else
{
d.rootNavigationController.PresentModalViewController(childBroswer, true);
}
Child browser class:
public partial class ChildBrowser : UIViewController
{
public string _url {get;set;}
UIActivityIndicatorView activityIndicator;
UIWebView webBrowser;
NSUrl nsURL;
NSUrlRequest nsURLRequest;
public ChildBrowser (string url) : base ("ChildBrowser", null)
{
nsURL = new NSUrl(url);
nsURLRequest = new NSUrlRequest(nsURL);
}
public override void DidReceiveMemoryWarning ()
{
// Releases the view if it doesn't have a superview.
base.DidReceiveMemoryWarning ();
// Release any cached data, images, etc that aren't in use.
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
if (webBrowser == null)
{
webBrowser = new UIWebView(new RectangleF(0,41,320,380));
}
this.Add(webBrowser);
webBrowser.LoadFinished += (object sender, EventArgs e) => {
activityIndicator.StopAnimating();
} ;
webBrowser.LoadStarted += (object sender, EventArgs e) => {
if (activityIndicator == null)
{
activityIndicator = new UIActivityIndicatorView(new RectangleF(UIScreen.MainScreen.Bounds.Width / 2 - 50,
UIScreen.MainScreen.Bounds.Height / 2 - 30,100,100));
activityIndicator.Color = UIColor.Black;
}
activityIndicator.StartAnimating();
this.Add(activityIndicator);
} ;
webBrowser.LoadHtmlString("<html><head></head><body></body></html>",null); //stringByEvaluatingJavaScriptFromString:#"document.body.innerHTML = \"\";"];
webBrowser.LoadRequest(nsURLRequest);
closeBrowser.TouchUpInside += handleClose;
// Perform any additional setup after loading the view, typically from a nib.
}
private void handleClose(object sender, EventArgs e)
{
webBrowser.Delegate = null;
webBrowser.StopLoading();
webBrowser.Dispose();
DismissViewController(true,delegate {
closeBrowser.TouchUpInside -= handleClose;
webBrowser.RemoveFromSuperview();
this.Dispose();
} );
}
public override void ViewWillAppear (bool animated)
{
base.ViewWillAppear (animated);
}
public override void ViewDidDisappear(bool animated)
{
base.ViewDidDisappear(true);
//webBrowser.RemoveFromSuperview();
}
public void webViewDidFinishLoad(UIWebView webView)
{
//string u = "";
}
protected override void Dispose(bool disposing)
{
ReleaseDesignerOutlets();
base.Dispose(disposing);
}
What am i missing?
Thank you!
Memory for native objects is not reclaimed when you call Dispose. Calling Dispose only drops the managed reference - but the ObjC runtime (reference counted) won't free the object until there's no native reference as well. See this Q&A for more details...
This means that code such as:
this.Add(webBrowser);
will create such a native reference and is likely (i.e. if not removed elsewhere) to keep the webBrowser instance alive as long as this is alive. If you keep Adding then you memory usage will keep growing.
I'm the OP - It seems that releasing the memory usage of UIWebView is quite hard, even when doing so in Objective-C.
What I have eventually done, which didn't solve the problem but improved the memory release is adding the following lines when closing the UIWebView:
NSUrlCache.SharedCache.RemoveAllCachedResponses();
NSUrlCache.SharedCache.DiskCapacity = 0;
NSUrlCache.SharedCache.MemoryCapacity = 0;
webView.LoadHtmlString("",null);
webView.EvaluateJavascript("var body=document.getElementsByTagName('body')[0];body.style.backgroundColor=(body.style.backgroundColor=='')?'white':'';");
webView.EvaluateJavascript("document.open();document.close()");
webView.StopLoading();
webView.Delegate = null;
webView.RemoveFromSuperview();
webView.Dispose();