I use a dependency service to force landscape for a single page in Android and iOS,
this is for Android:
public class OrientationService : IOrientationService
{
public void Landscape()
{
((Activity)Forms.Context).RequestedOrientation = ScreenOrientation.Landscape;
}
public void Portrait()
{
((Activity)Forms.Context).RequestedOrientation = ScreenOrientation.Portrait;
}
}
it works well and as required: forcing the landscape mode, even the the device orientation in hand is portrait, I need to achieve the same for iOS, tried this (tried also the commented code):
public class OrientationService : IOrientationService
{
public void Landscape()
{
UIDevice.CurrentDevice.SetValueForKey(new NSNumber((int)UIInterfaceOrientation.LandscapeLeft), new NSString("orientation"));
//((AppDelegate)UIApplication.SharedApplication.Delegate).CurrentOrientation = UIInterfaceOrientationMask.Landscape;
//UIApplication.SharedApplication.SetStatusBarOrientation(UIInterfaceOrientation.LandscapeLeft, false);
}
public void Portrait()
{
UIDevice.CurrentDevice.SetValueForKey(new NSNumber((int)UIInterfaceOrientation.Portrait), new NSString("orientation"));
//((AppDelegate)UIApplication.SharedApplication.Delegate).CurrentOrientation = UIInterfaceOrientationMask.Portrait;
//UIApplication.SharedApplication.SetStatusBarOrientation(UIInterfaceOrientation.Portrait, false);
}
}
but this only switch to landscape if the device position in landscape mode, not like the Android version
You should do something more in iOS
in AppDelegate.cs
public bool allowRotation;
And rewrite the method
public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations(UIApplication application, [Transient] UIWindow forWindow)
{
if(allowRotation==true)
{
return UIInterfaceOrientationMask.Landscape;
}
else
{
return UIInterfaceOrientationMask.Portrait;
}
}
in dependency service
public class OrientationService : IOrientationService
{
public void Landscape()
{
AppDelegate appDelegate = (AppDelegate)UIApplication.SharedApplication.Delegate;
appDelegate.allowRotation = true;
UIDevice.CurrentDevice.SetValueForKey(new NSNumber((int)UIInterfaceOrientation.LandscapeLeft), new NSString("orientation"));
//((AppDelegate)UIApplication.SharedApplication.Delegate).CurrentOrientation = UIInterfaceOrientationMask.Landscape;
//UIApplication.SharedApplication.SetStatusBarOrientation(UIInterfaceOrientation.LandscapeLeft, false);
}
public void Portrait()
{
AppDelegate appDelegate = (AppDelegate)UIApplication.SharedApplication.Delegate;
appDelegate.allowRotation = true;
UIDevice.CurrentDevice.SetValueForKey(new NSNumber((int)UIInterfaceOrientation.Portrait), new NSString("orientation"));
//((AppDelegate)UIApplication.SharedApplication.Delegate).CurrentOrientation = UIInterfaceOrientationMask.Portrait;
//UIApplication.SharedApplication.SetStatusBarOrientation(UIInterfaceOrientation.Portrait, false);
}
}
Related
I am using QuickLook to preview Images, Pdf and Microsoft office documents. It is working fine to preview documents but its ShouldOpenUrl delegate method not firing whenever i try to open link from documents. Following is the code that i tried.
I test my app with iPhone and iPad having iOS v11.
// Open documents using title and file url
public void OpenDocument(string title, string url)
{
var rootViewController = UIApplication.SharedApplication.KeyWindow.RootViewController;
var previewViewController = new QLPreviewController();
previewViewController.DataSource = new DocumentPreviewDataSource(title, url);
previewViewController.Delegate = new PreviewControllerDelegate();
rootViewController.PresentViewController(previewViewController, true, null);
}
// QLPreviewControllerDelegate Implementation
public class PreviewControllerDelegate : QLPreviewControllerDelegate
{
public override bool ShouldOpenUrl(QLPreviewController controller, NSUrl url, IQLPreviewItem item)
{
Console.WriteLine("PreviewControllerDelegate::ShouldOpenUrl: {0}", url.AbsoluteString);
return true;
}
}
You can use the weakdelegate
public partial class xxxViewController : UIViewController,IQLPreviewControllerDelegate,IQLPreviewControllerDataSource
//. . .
in method OpenDocument
public void OpenDocument()
{
var previewViewController = new QLPreviewController();
previewViewController.View.Frame = View.Bounds;
previewViewController.WeakDelegate = this;
previewViewController.WeakDataSource = this;
this.PresentViewController(previewViewController, true,null);
}
And override the method in QLPreviewControllerDelegate and QLPreviewControllerDataSource
public nint PreviewItemCount(QLPreviewController controller)
{
return 1;
}
public IQLPreviewItem GetPreviewItem(QLPreviewController controller, nint index)
{
return new NSUrl("your url");
}
[Export("previewController:shouldOpenURL:forPreviewItem:")]
public bool ShouldOpenUrl(QLPreviewController controller, NSUrl url, IQLPreviewItem item)
{
Console.WriteLine("PreviewControllerDelegate::ShouldOpenUrl: {0}", url.AbsoluteString);
return true;
}
[Export("previewControllerWillDismiss:")]
public void WillDismiss(QLPreviewController controller)
{
// do some thing
}
I use the above code and it works fine.
I am using MvvmCross in my Xamarin.Android application. I want to make my own custom MvxRecyclerAdapter so that I can have multiple buttons in each row of the MvxRecyclerView. Here is my custom MvxRecyclerView:
public class TwoPieceMvxRecyclerView : MvxRecyclerView
{
private bool _initialized;
public TwoPieceMvxRecyclerView(Context context, IAttributeSet attr) : base(context, attr)
{
}
public override Android.Support.V7.Widget.RecyclerView.Adapter GetAdapter()
{
if(!_initialized)
{
SetAdapter(new TwoPieceMvxRecyclerAdapter());
_initialized = true;
}
return base.GetAdapter();
}
}
And here is my custom MvxRecyclerAdapter:
public class TwoPieceMvxRecyclerAdapter : MvxRecyclerAdapter, IOnClickListener
{
private ICommand _itemClickPiece1;
private ICommand _itemClickPiece2;
private View _clickablePiece1;
private View _clickablePiece2;
public TwoPieceMvxRecyclerAdapter()
{
}
public ICommand ItemClickPiece1
{
get { return _itemClickPiece1; }
set
{
if (ReferenceEquals(_itemClickPiece1, value))
{
return;
}
_itemClickPiece1 = value;
}
}
public ICommand ItemClickPiece2
{
get { return _itemClickPiece2; }
set
{
if (ReferenceEquals(_itemClickPiece2, value))
{
return;
}
_itemClickPiece2 = value;
}
}
protected override Android.Views.View InflateViewForHolder(Android.Views.ViewGroup parent, int viewType, MvvmCross.Binding.Droid.BindingContext.IMvxAndroidBindingContext bindingContext)
{
var view = base.InflateViewForHolder(parent, viewType, bindingContext);
_clickablePiece1 = view.FindViewById<View>(Resource.Id.clickable_piece1);
_clickablePiece2 = view.FindViewById<View>(Resource.Id.clickable_piece2);
_clickablePiece1.SetOnClickListener(this);
_clickablePiece2.SetOnClickListener(this);
return view;
}
public void OnClick(View v)
{
if (v == _clickablePiece1)
{
ItemClickPiece1.Execute(null);
}
else if (v == _clickablePiece2)
{
ItemClickPiece2.Execute(null);
}
}
}
When I run the application I get this error:
Could not activate JNI Handle 0xbfd00978 (key_handle 0x6e44919) of
Java type
'md5bd77c484e80df14e69d8c5ab04394fe0/TwoPieceMvxRecyclerView' as
managed type
'AzzimovMobile.Droid.Components.TwoPieceMvxRecycler.TwoPieceMvxRecyclerView'.
System.InvalidOperationException: If you wan't to use single
item-template RecyclerView Adapter you can't change
it'sIMvxTemplateSelector to anything other than
MvxDefaultTemplateSelector
You are missing a constructor on your RecyclerView:
public TwoPieceMvxRecyclerView(IntPtr javaReference, JniHandleOwnership transfer): base(javaReference, transfer)
{
}
Also be aware you don't need to use a custom RecyclerView to change its Adapter. You can just grab the RecyclerView instance on your .cs view and set the adapter from there. Something like this should work:
public class MyView: MvxFragment<MyViewModel>
{
//...
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
var view = base.OnCreateView(inflater, container, savedInstanceState);
// ...
var recycler = view.FindViewById<MvxRecyclerView>(Resource.Id.recycler);
recycler.Adapter = new TwoPieceMvxRecyclerAdapter(((IMvxAndroidBindingContext)BindingContext);
// you can even set a TemplateSelector here!
recycler.ItemTemplateSelector = new MyTemplateSelector();
// ...
return view;
}
}
I am developing an App using Xamarin.Forms for listing the news from different sources. I use a webView to open the link corresponding to the news. But I want to show the progress while loading the webpage into web view, like the progress bar on Safari App. For this I have used the ProgressBar element like this:
<StackLayout>
<!-- WebView needs to be given height and width request within layouts to render. -->
<ProgressBar Progress ="" HorizontalOptions="FillAndExpand" x:Name="progress"/>
<WebView x:Name="webView"
HeightRequest="1000"
WidthRequest="1000"
VerticalOptions= "FillAndExpand"
Navigating="webOnNavigating"
Navigated="webOnEndNavigating"/>
</StackLayout>
and in the code I have used
void webOnNavigating (object sender, WebNavigatingEventArgs e)
{
progress.IsVisible = true;
}
void webOnEndNavigating (object sender, WebNavigatedEventArgs e)
{
progress.IsVisible = false;
}
But I want to show also the progress of loading the data, not just an indication that is loading and load. I want the user to know that the data are loading. Is there a way to achieve this.
The implementations should be platform specific via custom renders. Luckily this topics has been discussed already for different platforms here on SO.
The Android version based on this thread:
[assembly: ExportRenderer(typeof(WebView), typeof(GenericWebViewRenderer))]
namespace WebViewWithProgressBar.Droid
{
public class GenericWebViewRenderer : WebViewRenderer
{
Context ctx;
public GenericWebViewRenderer(Context context) : base(context)
{
ctx = context;
}
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
{
base.OnElementChanged(e);
if (Control == null)
return;
var progressBar = new Android.Widget.ProgressBar(ctx, null, Android.Resource.Attribute.ProgressBarStyleHorizontal);
Control.SetWebChromeClient(new MyWebChromeClient(progressBar));
Control.AddView(progressBar);
}
class MyWebChromeClient : Android.Webkit.WebChromeClient
{
Android.Widget.ProgressBar progressBar;
public MyWebChromeClient(Android.Widget.ProgressBar progressBar)
{
this.progressBar = progressBar;
}
public override void OnProgressChanged(Android.Webkit.WebView view, int newProgress)
{
progressBar.SetProgress(newProgress, true);
}
}
}
}
On iOS it is a bit trickier, here is a very simple mock that does it job pretty well:
[assembly: ExportRenderer(typeof(WebView), typeof(GenericWebViewRenderer))]
namespace WebViewWithProgressBar.iOS
{
public class GenericWebViewRenderer : ViewRenderer<WebView, UIWebView>
{
protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
{
base.OnElementChanged(e);
if (Control == null)
{
var progressBar = new UIProgressView(UIProgressViewStyle.Bar);
progressBar.TintColor = UIColor.Green;
progressBar.TrackTintColor = UIColor.Black;
progressBar.ProgressTintColor = UIColor.Red;
var webView = new UIWebView(Frame);
webView.AddSubview(progressBar);
SetNativeControl(webView);
Control.Delegate = new MyUIWebViewDelegate(progressBar);
webView.LoadRequest(new NSUrlRequest(new NSUrl("https://google.com")));
}
}
class MyUIWebViewDelegate : UIWebViewDelegate
{
UIProgressView progressBar { get; }
public MyUIWebViewDelegate(UIProgressView progressBar)
{
this.progressBar = progressBar;
}
public override void LoadStarted(UIWebView webView)
{
progressBar.SetProgress(0.1f, false);
}
public override void LoadingFinished(UIWebView webView)
{
progressBar.SetProgress(1.0f, true);
}
public override void LoadFailed(UIWebView webView, NSError error)
{
// TODO:
}
}
}
}
For more details please check here.
P.S.: This code examples are available on github.
I'm trying out MvvmCross with Xamarin 'classic'.
I've got it working with Android.
But I can't get it work for iOS. I've taken a look at the sample mentioned here (eh): MVVMCross support for Xamarin.iOS Storyboards
I'm really missing something.
What do i have:
A storyboard with only 3 controls on it. a label and 2 buttons. All 3
have names so i get the properties in the RootViewController class.
The basis setup.cs
AppDelegate.cs
[Register("AppDelegate")]
public partial class AppDelegate : MvxApplicationDelegate
{
UIWindow _window;
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
_window = new UIWindow(UIScreen.MainScreen.Bounds);
StoryBoardTouchViewPresenter sbPresenter = new StoryBoardTouchViewPresenter(this, _window, "MainStoryboard");
var setup = new Setup(this, _window);
setup.Initialize();
var startup = Mvx.Resolve<IMvxAppStart>();
startup.Start();
sbPresenter.MasterNavigationController.NavigationBar.Translucent = false;
sbPresenter.MasterNavigationController.SetNavigationBarHidden(false, false);
return true;
}
}
StoryBoardTouchViewPresenter (from MVVMCross: Is it possible to use Storyboard with ICommand navigation?) But the API is changed.
public class StoryBoardTouchViewPresenter : MvxTouchViewPresenter
{
public static UIStoryboard Storyboard = null;
public StoryBoardTouchViewPresenter(UIApplicationDelegate applicationDelegate, UIWindow window, string storyboardName, NSBundle StoryboardBundleOrNull = null)
: base(applicationDelegate, window)
{
Storyboard = UIStoryboard.FromName(storyboardName, StoryboardBundleOrNull);
}
public override void Show(IMvxTouchView view)
{
MvxViewController sbView = null;
try
{
sbView = (MvxViewController)Storyboard.InstantiateViewController(view.Request.ViewModelType.Name.Replace("Model", ""));
}
catch (Exception e)
{
Console.WriteLine("Failed to find storyboard view, did you forget to set the Storyboard ID to the ViewModel class name without the Model suffix ?" + e);
}
sbView.Request = view.Request;
base.Show(sbView);
}
}
The default App.cs in the Core project
public class App : Cirrious.MvvmCross.ViewModels.MvxApplication
{
public override void Initialize()
{
CreatableTypes()
.EndingWith("Service")
.AsInterfaces()
.RegisterAsLazySingleton();
RegisterAppStart<ViewModels.MainViewModel>();
}
}
The ViewModel:
public class MainViewModel : MvxViewModel
{
ITodoTaskService taskService;
IDataManager<TodoTask> tasks;
public MainViewModel(ITodoTaskService taskService)
{
this.taskService = taskService;
}
public async override void Start()
{
this.tasks = new DataManager<TodoTask>(await this.taskService.GetTodoTasksAsync());
this.tasks.MoveFirst();
Rebind();
base.Start();
}
private void Rebind()
{
this.Description = this.tasks.Current.Description;
NextCommand.RaiseCanExecuteChanged();
PreviousCommand.RaiseCanExecuteChanged();
}
private string description;
public string Description
{
get { return this.description; }
set
{
this.description = value;
RaisePropertyChanged(() => Description);
}
}
private MvxCommand nextCommand;
public MvxCommand NextCommand
{
get
{
this.nextCommand = this.nextCommand ?? new MvxCommand(NavigateToNext, CanNavigateNext);
return this.nextCommand;
}
}
private bool CanNavigateNext()
{
return this.tasks.CanMoveNext;
}
public void NavigateToNext()
{
this.tasks.MoveNext();
Rebind();
}
private MvxCommand previousCommand;
public MvxCommand PreviousCommand
{
get
{
this.previousCommand = this.previousCommand ?? new MvxCommand(NavigateToPrevious, CanNavigatePrevious);
return this.previousCommand;
}
}
private bool CanNavigatePrevious()
{
return this.tasks.CanMovePrevious;
}
public void NavigateToPrevious()
{
this.tasks.MovePrevious();
Rebind();
}
}
I tried all kind of things. At the moment i get an exception that the MainView cannot be found. Which i partly understand. in App.cs MainViewModel is the start up. But the controller is called RootViewController. I think the RootviewController should bind to my MainViewModel. But i don't know how.
How should I make MvvmCross with iOs working?
How should I name the parts?
MvvmCross' default view finder will look for a view called MainView. That view should be derived from MvxViewController or another IMvxTouchView type. If you don't want to name your view controller "MainView" then you need to create a custom view resolver.
My advice: just rename your RootViewController to MainView.
I'm using RoboVM bindings for my iOS application to display AdMob interstitials. When I close the interstitial ad, I lose all touch controls. Is there a way to detect the ad being closed so I can put the touch back to the game? Or is there a better way to implement interstitials? Here's my code below:
public class IOSLauncher extends IOSApplication.Delegate implements IActivityRequestHandler{
private static final Logger log = new Logger(IOSLauncher.class.getName(), Application.LOG_DEBUG);
private IOSApplication iosApplication;
//interstitial
private static final String INTERSTITIAL_AD = "MY_AD_ID";
private GADInterstitial interstitial;
private UIWindow window;
private UIViewController rootViewController;
#Override
protected IOSApplication createApplication() {
IOSApplicationConfiguration config = new IOSApplicationConfiguration();
config.orientationLandscape = true;
config.orientationPortrait = false;
iosApplication = new IOSApplication(new PaperPig(this), config);
return iosApplication;
}
public static void main(String[] argv) {
NSAutoreleasePool pool = new NSAutoreleasePool();
UIApplication.main(argv, null, IOSLauncher.class);
pool.close();
}
#Override
public void initializeAds() {
intializeInterstitial();
}
public void intializeInterstitial () {
rootViewController = new UIViewController();
interstitial = new GADInterstitial();
interstitial.setAdUnitID(INTERSTITIAL_AD);
interstitial.setDelegate(new GADInterstitialDelegateAdapter() {
#Override
public void didReceiveAd (GADInterstitial ad) {
System.out.println("Did receive ad.");
}
#Override
public void didFailToReceiveAd (GADInterstitial ad, GADRequestError error) {
System.out.println(error.description());
System.out.println(error.getErrorCode());
}
});
window = new UIWindow(UIScreen.getMainScreen().getBounds());
window.setRootViewController(rootViewController);
window.addSubview(rootViewController.getView());
interstitial.loadRequest(GADRequest.create());
}
#Override
public void showOrLoadInterstital() {
if (interstitial.isReady()) {
if (rootViewController == null) {
rootViewController = new UIViewController();
}
if (window == null) {
window = new UIWindow(UIScreen.getMainScreen().getBounds());
window.setRootViewController(rootViewController);
}
window.makeKeyAndVisible();
interstitial.present(rootViewController);
}
//Return touch back to Game
//UIApplication.getSharedApplication().getKeyWindow().setRootViewController(rootViewController);
}
}
You need to call:
window.setHidden(true);
Change your creation of GADInterstitialDelegateAdapter() to the following
interstitial.setDelegate(new GADInterstitialDelegateAdapter() {
#Override
public void didReceiveAd (GADInterstitial ad) {
System.out.println("Did receive ad.");
}
#Override
public void didDismissScreen(GADInterstitial ad) {
window.setHidden(true);
}
#Override
public void didFailToReceiveAd (GADInterstitial ad, GADRequestError error) {
System.out.println(error.description());
System.out.println(error.getErrorCode());
}
});