Monday, April 27, 2015

MVVMCross Extended Splash Screen Crossplatform (iOS/Android/WP)

Problem:
Trying to get the splash screen of the application to run longer then the default for each platform.  The typical answer I see posted on most sites is to use the default properties for each of the platform projects.  I wanted something I had more control of.  We are using the MVVMCross pattern for the application and so I wanted to put the solution into the PCL for use on all 3 platforms.  This solution creates a View with the splash screen image which can be extended via the timer function.  This allows us to display the image as long or short as we need it.


Solution:
I found several sites with tips and some hints from Stuart Lodge.  I managed to come up with a solution which builds on some code I found over at the Xamarin developer site.  The timer class should be placed in the PCL of your project.  It can then be referenced by your ModelView and this in turn binds to the View.

1.) Timer Class

using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace iReport.Core
{
 

    /// <summary>
    /// Missing from PCL, except if targeting .NET 4.5.1 + Win8.1 + WP8.1
    /// </summary>
    internal sealed class Timer : CancellationTokenSource
    {
        internal Timer(Action<object> callback, object state, int millisecondsDueTime, int millisecondsPeriod, bool waitForCallbackBeforeNextPeriod = false)
        {
            //Contract.Assert(period == -1, "This stub implementation only supports dueTime.");

            Task.Delay(millisecondsDueTime, Token).ContinueWith(async (t, s) =>
            {
                var tuple = (Tuple<Action<object>, object>)s;

                while (!IsCancellationRequested)
                {
                    if (waitForCallbackBeforeNextPeriod)
                        tuple.Item1(tuple.Item2);
                    else
                        Task.Run(() => tuple.Item1(tuple.Item2));

                    await Task.Delay(millisecondsPeriod, Token).ConfigureAwait(false);
                }

            }, Tuple.Create(callback, state), CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Default);
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
                Cancel();

            base.Dispose(disposing);
        }
    }


}

2.)Model View

using Cirrious.MvvmCross.ViewModels;
using iReport.Core.Services.Collections;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;


namespace iReport.Core.ViewModels
{
    public class TimerViewModel : MvxViewModel
    {
        private DateTime? _whenToFinish;
        private Timer _timer;
        public TimerViewModel()
        {
                    _timer = new Timer(OnTick, null, 1000, 1000);
                    _whenToFinish = DateTime.UtcNow.AddSeconds(3);
        }

        #region Timer Test
            private void OnTick(object state)
            {
                if (!_whenToFinish.HasValue)
                    return;

                if (DateTime.UtcNow >= _whenToFinish.Value)
                {
                    base.ShowViewModel<MainViewModel>();

                    _whenToFinish = null;
                }
            }

        
        
        #endregion

    }
}


3.)View


using Cirrious.MvvmCross.Binding.BindingContext;
using Cirrious.MvvmCross.Touch.Views;
using Cirrious.MvvmCross.ViewModels;
using iReport.Core.ViewModels;
using iReport.Touch.Controls;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;

namespace iReport.Touch.Views
{

    [Register("TimerView")]
    public class TimerView : MvxViewController
    {
        private BindableProgress _bindableProgress;


        public override void ViewDidLoad()
        {

            View = new UIView() { BackgroundColor = UIColor.Gray };

            base.ViewDidLoad();

            this.NavigationController.ToolbarHidden = true;
            this.NavigationController.NavigationBarHidden = true;

            UIImage splashscreen = MaxResizeImage(UIImage.FromFile("Default.png", UIScreen.MainScreen.Bounds.Width,UiScreen.MainScreen.Bounds.Height);

             UIImageView splashView = new UIImageView(splashscreen);
             Add(splashView);

            var set = this.CreateBindingSet<TimerView, Core.ViewModels.TimerViewModel>();
           

          
            set.Apply();

        }
    }
 // resize the image to be contained within a maximum width and height, keeping aspect ratio
public UIImage MaxResizeImage(UIImage sourceImage, float maxWidth, float maxHeight)
{
    var sourceSize = sourceImage.Size;
    var maxResizeFactor = Math.Max(maxWidth / sourceSize.Width, maxHeight / sourceSize.Height);
    if (maxResizeFactor > 1) return sourceImage;
    var width = maxResizeFactor * sourceSize.Width;
    var height = maxResizeFactor * sourceSize.Height;
    UIGraphics.BeginImageContext(new SizeF(width, height));
    sourceImage.Draw(new RectangleF(0, 0, width, height));
    var resultImage = UIGraphics.GetImageFromCurrentImageContext();
    UIGraphics.EndImageContext();
    return resultImage;
}



}

Source:

  1. http://stackoverflow.com/questions/5429746/how-to-get-width-and-height-of-iphone-ipad-using-monotouch
  2. http://forums.xamarin.com/discussion/4170/resize-images-and-save-thumbnails
  3. http://forums.xamarin.com/discussion/27327/ios-background-image-scale
  4. http://stackoverflow.com/questions/20447787/system-threading-timer-issue-only-timer-in-mvvmcross
  5. http://stackoverflow.com/questions/15961664/viewmodel-lifecycle-when-does-it-get-disposed
  6. https://code.msdn.microsoft.com/windowsapps/Splash-screen-sample-89c1dc78
 

No comments:

Post a Comment