FZ2CL: InvoiceJe: iOS

Let's start with the iOS implementation.

Setup

First things first:

If you followed the previous tutorial, you'll need to inherit the AppDelegate class from MvxApplicationDelegate (as per outlined in the TipCalc tutorial, the Nuget didn't help up update this part :( ). 

Next, you'll need to override the FinishedLaunching function to this:

 public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
        {
            // Override point for customization after application launch.
            // If not required for your application you can safely delete this method
            Window = new UIWindow(UIScreen.MainScreen.Bounds);

            var presenter = new MvxIosViewPresenter(this, Window);

            var setup = new Setup(this, presenter);
            setup.Initialize();

            var startup = Mvx.Resolve<IMvxAppStart>();
            startup.Start();

            Window.MakeKeyAndVisible();

            return true;
        }

Once you've done the above, let's start adding the MainView, ConfigurationView and InvoicesView. Simply Right Click -> Add Class and add a StoryboardViewController for each of the 3 views. Remember to  add a ViewController to each Storyboard and change the class and storyboardId to each respective MainView, ConfigurationView and InvoicesView.

And for each MainView, ConfigurationView and InvoicesView, add the [MvxFromStoryboard] attribute to the class.

Okay, just so we can differentiate between ConfigurationView and InvoicesView when we switch tabs, at each storyboard, add a label with the text 'Configuration' and 'Invoices' respectively.

Alright with that out of the way, we can start actually implementing the TabBar.

MvxIosPresenter

In MvvmCross5, they have added a new default presenter which makes our life a whole lot easier in implementing the default navigation methods (tabbar, stack and modal). Right now, we'll be playing around with the TabBar. More info can be found at MvvmCross' documentation.

Set Root Presentation

Just like in the Android project, our MainView will be our Root.

So in MainView.cs, add the [MvxRootPresentation] attribute.

And since InvoicesView and ConfigurationView will be our tabs, then:

Add [MvxTabPresentation(TabName = "Configuration")] to ConfigurationView

Add [MvxTabPresentation(TabName = "Invoices")] to InvoicesView

That's it. We're done! Well, not so.

If you try to run the app, you'll only get a blank screen.

This ViewPresenter have a gotcha, which isn't very intuitive, in which we NEED to call the ShowViewModel() method to each child views before the root view will show the links to each child views in the tab bar.

Hence, let's do that.

Inside the MainViewModel(), let's add the function ShowInitialViewModelsCommand() that will be used by our root view, like so:

        private IMvxCommand _showInitialViewModelsCommand;
        public IMvxCommand ShowInitialViewModelsCommand
        {
            get
            {
                return _showInitialViewModelsCommand ?? (_showInitialViewModelsCommand = new MvxCommand(ShowInitialViewModels));
            }
        }
        private void ShowInitialViewModels()
        {
            ShowViewModel<InvoicesViewModel>();
            ShowViewModel<ConfigurationViewModel>();
        }

...And in our MainView.cs, let's override the ViewWillAppear method and trigger the ShowInitialViewModelsCommand. We'll add a bit more functionality so that the command will only be triggered at the first time the view is seen by the user.

        private bool _isPresentedFirstTime = true;

        public override void ViewWillAppear(bool animated)
        {
            base.ViewWillAppear(animated);

            if (ViewModel != null && _isPresentedFirstTime)
            {
                _isPresentedFirstTime = false;
                ViewModel.ShowInitialViewModelsCommand.Execute(null);
            }

        }

 

Now that we're done, try running your iOS app again. It should work this time :)

Adding a divider to recycler view

There are two ways:

One would be to simply add an empty view with a 12% black background to the end of each item in RecyclerView. Like this:

<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/customDividerColor"/>

The other way would be to define the ItemDecoration in the RecyclerView, as per outlined in this StackOverflow question:

https://stackoverflow.com/questions/31242812/how-can-a-divider-line-be-added-in-an-android-recyclerview

 

The elements you can override in Theme.AppCompat

ref: https://stackoverflow.com/questions/26702000/change-status-bar-color-with-appcompat-actionbaractivity

<!-- ============= -->
<!-- Color palette --> <!-- ============= --> <!-- The primary branding color for the app. By default, this is the color applied to the action bar background. --> <attr name="colorPrimary" format="color" /> <!-- Dark variant of the primary branding color. By default, this is the color applied to the status bar (via statusBarColor) and navigation bar (via navigationBarColor). --> <attr name="colorPrimaryDark" format="color" /> <!-- Bright complement to the primary branding color. By default, this is the color applied to framework controls (via colorControlActivated). --> <attr name="colorAccent" format="color" /> <!-- The color applied to framework controls in their normal state. --> <attr name="colorControlNormal" format="color" /> <!-- The color applied to framework controls in their activated (ex. checked) state. --> <attr name="colorControlActivated" format="color" /> <!-- The color applied to framework control highlights (ex. ripples, list selectors). --> <attr name="colorControlHighlight" format="color" /> <!-- The color applied to framework buttons in their normal state. --> <attr name="colorButtonNormal" format="color" /> <!-- The color applied to framework switch thumbs in their normal state. --> <attr name="colorSwitchThumbNormal" format="color" />

HTML to PDF

You can use wkhtmltopdf

C# binding that works is this:

https://github.com/gmanny/Pechkin

However, it has a problem where it prints based on 800x600, because it's hard-coded to that:

https://github.com/wkhtmltopdf/wkhtmltopdf/issues/1508

So, you'll need to make your html page at 800x600 resolution. Hua.

For images, you need to put the full file path if you're using windows, eg:

 file:\\\E:\misc\sdfsdf\prog\WindowsFormsApplication1\WindowsFormsApplication1\bin\Debug\links\logo.png

Example code:

            var html = File.ReadAllText("invoice_light_green.html");

            var objectConfig = new ObjectConfig();
            objectConfig.SetPrintBackground(true)
                .SetLoadImages(true)
                .SetAllowLocalContent(true)
                .SetScreenMediaType(true);

            var globalConfig = new GlobalConfig();
            globalConfig.SetPaperSize(System.Drawing.Printing.PaperKind.A4);
            globalConfig.SetMarginLeft(0).SetMarginRight(0).SetMarginTop(0).SetMarginBottom(0);

            //var asdf = new System.Drawing.Printing.PaperSize("asd", 1200, 1600);
            //globalConfig.SetPaperSize(asdf);

            byte[] pdfBuf = new SimplePechkin(globalConfig).Convert(objectConfig, html);

            File.WriteAllBytes("meow.pdf", pdfBuf);