Android.Basics – Adding a Bottom Navigation View

Changing my pace of steady Xamarin content to go to my roots of native mobile development. This time, we’ll look at implementing the latest control provided by Google and Material Design – The BottomNavigationView.

Resources

Aren’t These Just Tabs?

I mean… yeah, but… it’s new and cool! Google finally realized that stretching to the top of the app can be annoying.
Screen Shot 2017-06-27 at 5.56.48 PM.png

This new control is also a little different from the TabLayout we all know in love from Material Design and Android development in that it is limited to 5 maximum tab items and does not support scrolling. It’s meant to act as top level or sibling level navigation as long as all items are of equal importance in the context of the application/current view. It is also nice to give some variety to our applications navigation scheme; larger apps with many tabbed views can become overwhelming, so tossing something new is relieving to our users.

Code

There are 3 major components to setting up a view with a BottomNavigationView.

  1. First, we need to create a menu resource for our navigation items.
  2. Then we need to create, style, and set up our BottomNavigationView in our layout.
  3. Lastly, add listeners for when an item is selected in our BottomNavigationView and make sure it fits the experience expectation defined in Material Design.

Create a Menu

In our example, we will be building an application for viewing adoptable puppies. Each navigation item will be a different set of these puppies by categorizing them. Let’s create a menu for “all”, “big”, “small”, “trained”, and “active” as categories for our puppies:

res/menu/bottom_bar_menu.xml

<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/all_puppies"         android:title="@string/action_all"         android:icon="@drawable/ic_home_white_24dp" />

    <item android:id="@+id/big_puppies"         android:title="@string/action_big"         android:icon="@drawable/ic_dog_white_24dp" />

    <item android:id="@+id/small_puppies"         android:title="@string/action_small"         android:icon="@drawable/ic_small_dog_white_24dp" />

    <item android:id="@+id/trained_puppies"         android:title="@string/action_trained"         android:icon="@drawable/ic_trained_white_24dp" />

    <item android:id="@+id/active_puppies"         android:title="@string/action_active"         android:icon="@drawable/ic_active_white_24dp" />
</menu>

With our menu, we can create our layout.

Updating the Layout

In our example, we are moving from a TabLayout with a ViewPager. However, the Material Design documentation for the BottomNavigationView states that it should NOT be used with side-swiping actions such as a ViewPager. Let’s replace that ViewPager with a FrameLayout that will be used to swap our active Fragment and also remove the TabLayout that is being replaced by the BottomNavigationView:

res/layout/activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"     xmlns:app="http://schemas.android.com/apk/res-auto"     xmlns:tools="http://schemas.android.com/tools"     android:id="@+id/main_content"     android:layout_width="match_parent"     android:layout_height="match_parent"     android:fitsSystemWindows="true"     tools:context="com.suavepirate.bottomnavigationpuppies.activities.MainActivity">

    <android.support.design.widget.AppBarLayout         android:id="@+id/appbar"         android:layout_width="match_parent"         android:layout_height="wrap_content"         android:paddingTop="@dimen/appbar_padding_top"         android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar             android:id="@+id/toolbar"             android:layout_width="match_parent"             android:layout_height="?attr/actionBarSize"             android:background="?attr/colorPrimary"             app:layout_scrollFlags="scroll|enterAlways"             app:popupTheme="@style/AppTheme.PopupOverlay">

        </android.support.v7.widget.Toolbar>
    </android.support.design.widget.AppBarLayout>

<FrameLayout     android:id="@+id/container"     android:layout_width="match_parent"     android:layout_height="match_parent"     app:layout_behavior="@string/appbar_scrolling_view_behavior" ></FrameLayout>
<android.support.design.widget.BottomNavigationView     android:id="@+id/bottombar"     android:layout_width="match_parent"     android:layout_height="56dp"     android:layout_gravity="bottom|fill_horizontal|start"     app:menu="@menu/bottom_bar_menu"     android:background="@android:color/white"     app:elevation="8dp"/>
</android.support.design.widget.CoordinatorLayout>

It’s important to layout the BottomNavigationView at the bottom of the page as well as give it a solid background and elevation. Also, notice how we apply our menu we created to the view by setting app:menu="@menu/bottom_bar_men".

With our layout set, let’s wire up listeners to update the current Fragment based on the selected navigation item.

Setting Up Listeners

In our MainActivity.java we can implement the BottomNavigationView.OnNavigationItemSelectedListener interface and override the onNavigationItemSelected method:

MainActivity.java

// imports

public class MainActivity extends AppCompatActivity implements BottomNavigationView.OnNavigationItemSelectedListener {
    private PageAdapter mSectionsPagerAdapter;
    private FrameLayout mContainer;
    private BottomNavigationView mBottomBar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        // Create the adapter that will return a fragment for each of puppy types
        mSectionsPagerAdapter = new PageAdapter(getSupportFragmentManager(), this);

        // Set up the ViewPager with the sections adapter.
        mContainer = (FrameLayout) findViewById(R.id.container);

        // set up the first Fragment
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        ft.add(R.id.container, mSectionsPagerAdapter.getItem(0), "CURRENT_PAGE");
        ft.commit();

        // set up the bottom bar and listener
        mBottomBar = (BottomNavigationView)findViewById(R.id.bottombar);
        mBottomBar.setOnNavigationItemSelectedListener(this);

    }

    // Handles when an item is selected to update the fragment container
    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        ft.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out);

        switch(item.getItemId()){
            case R.id.all_puppies: ft.replace(R.id.container, mSectionsPagerAdapter.getItem(0));
                break;
            case R.id.big_puppies: ft.replace(R.id.container, mSectionsPagerAdapter.getItem(1));
                break;
            case R.id.small_puppies: ft.replace(R.id.container, mSectionsPagerAdapter.getItem(2));
                break;
            case R.id.trained_puppies: ft.replace(R.id.container, mSectionsPagerAdapter.getItem(3));
                break;
            case R.id.active_puppies: ft.replace(R.id.container, mSectionsPagerAdapter.getItem(4));
                break;
        }
        ft.commit();
        return true;
    }
}

Now with all of this, we are able to switch the current Fragment with a fade in and out animation when the selected navigation item is updated. That means our BottomNavigationView is implemented and ready to go!

Screenshot_1497932698

If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.

Advertisements

Xamarin.Basics – Ad Hoc iOS Builds, Part 1: Certificates and Profiles

For those who are newer to iOS application development and are trying to run their app on devices or deploy it to others’ devices, let me first offer you my condolences. Now let’s look at the overwhelming number of steps required to do a basic build on an iOS device!

Resources Required Before Starting

  1. A Mac. You can’t do this without an actual Mac device running OSX
  2. XCode installed on the Mac
  3. An Apple Developer license (read below)

Join the Apple Developer Program

In order to distribute apps, you’ll need to create certificates and profiles in the end. Before you can do that, you’ll need to join the Apple developer program for iOS. Keep in mind, this can take time (multiple days).

Head over to https://developer.apple.com/programs/ and click on the Enroll button.

apple_enroll

You’ll see an intimidating following something like this:

Enrolling as an Individual

If you are an individual or sole proprietor/single person business, sign in with your Apple ID to get started. You’ll need to provide basic personal information, including your legal name and address.

Enrolling as an Organization

If you’re enrolling your organization, you’ll need an Apple ID as well as the following to get started:

 
A D-U-N-S® Number

Your organization must have a D-U-N-S Number so that we can verify your organization’s identity and legal entity status. These unique nine-digit numbers are assigned by Dun & Bradstreet and are widely used as standard business identifiers. You can check to see if your organization already has a D-U-N-S Number and request one if necessary. They are free in most jurisdictions. Learn more

 
Legal Entity Status

Your organization must be a legal entity so that it can enter into contracts with Apple. We do not accept DBAs, fictitious businesses, trade names, or branches.

Legal Binding Authority

As the person enrolling your organization in the Apple Developer Program, you must have the legal authority to bind your organization to legal agreements. You must be the organization’s owner/founder, executive team member, senior project lead, or have legal authority granted to you by a senior employee.

Follow all the steps for either an individual or organization. PS. This costs about $100 (as of June 2017). Once you’ve submitted your enrollment application, an Apple employee will review your request and get back to you within 1-2 business days (usually) with your accepted (or declined) enrollment. Once this is done, you’ll be able to access the developer portal where you can add other members of your organization, manage certificates, apps, devices, and profiles.

Getting the Proper Requirements for Ad Hoc

Now that you can access the portal, go to the Developer portal and click on Account to sign in with your Apple ID.

After signing in, you’ll see some options on the left navigation bar. Click on Certificates, IDs, and Profiles

Components You Will Get From This Post

There are 4 major components you will need in order to be able to deploy an app to multiple devices:

  1. An App ID
  2. An Ad Hoc Certificate – created, downloaded, and installed on the machine.
  3. The UDIDs of every iOS device that will run the app
  4. A Provisioning Profile that combines all three things

Creating an App ID

In the developer portal Certificates, IDs, and Profiles section, click on App IDs from the left menu, then click the plus button in the top right:
Screen Shot 2017-06-21 at 10.33.47 AMScreen Shot 2017-06-21 at 10.23.59 AMScreen Shot 2017-06-21 at 10.24.04 AM

Then fill out the fields for Name and Bundle Identifier.
Screen Shot 2017-06-21 at 10.24.22 AMScreen Shot 2017-06-21 at 10.24.36 AM

Scroll on down and select the additional App Services this app will need to support:Screen Shot 2017-06-21 at 10.24.50 AM

Click Continue and you’ll be done creating your App ID and can continue to creating a certificate.

Creating a Distribution Certificate

Over in the left navigation, click on Certificates > Production then click the plus button in the top right:Screen Shot 2017-06-21 at 10.25.06 AMScreen Shot 2017-06-21 at 10.25.10 AM

In the Production select App Store and Ad Hoc, then scroll down to Continue:

Screen Shot 2017-06-21 at 10.25.20 AM

You’ll then see a page that explains how to create a Certificate Signing Request that is required to generate the Certificate. Click Continue, then in the Mac open the Keychain Access application.
Then from the Keychain Access menu, go to Certificate Assistant and Request a Certificate from a Certificate Authority:
cert_request

Fill out the form and save the request file locally:
Screen Shot 2017-06-21 at 10.26.09 AM

Take this newly saved file and go back to the Apple Developer Portal and upload it:

upload_cert

Click Continue and you’ll now be able to download the Certificate. Double click / open the newly downloaded certificate, and it will be installed into your keychain.

Important Notes About Certificates

Guess what! It gets more complicated and annoying!

Once you’ve created a certificate, you can ONLY USE IT on the machine that created the signing request due to the signing mechanism. In order to allow a user to use the certificate, you’ll need to export it to a .p12 file which is then signed by a new password. The other Mac user can then take that .p12 file and save it to their keychain using said password. We won’t go into depth about this process in this post, but perhaps in a follow-up.

Adding iOS Devices

In order to install an ad-hoc or non-App Store iOS app, the devices that will install and run the app need to be registered and eventually included in part of the build process. In the Apple Developer Portal we can add devices to our profile (up to 100 devices).

In the left navigation, click on Devices > All then once again click the plus button in the top right:Screen Shot 2017-06-21 at 10.26.56 AM

Give your new device a Name and then get the UDID of the device.

Getting the UDID of an iOS Device

Plug in your iOS device to your Mac and open iTunes if it doesn’t open automatically. In iTunes, click on the device button to get to the device details:Screen Shot 2017-06-21 at 11.50.49 AM.png

You’ll notice that there is no UDID field! Here’s the secret: click the device’s Serial Number to reveal the UDID:

Copy the UDID.

Back in the Apple Developer Portal add the UDID and click Continue to save the device.

Now that we have an App ID, Certificate, and our devices, the last step is to create a Provisioning Profile and install it on the Mac.

Creating an Ad Hoc Provisioning Profile

In the left navigation, click on Provisioning Profiles > Distribution and then the plus button in the top right.Screen Shot 2017-06-21 at 10.33.16 AM

Next you’ll be asked which type of Profile you want to create. Select Ad Hoc under the Distribution section.Screen Shot 2017-06-21 at 10.33.22 AM

Click Continue and you’ll then need to select which App ID this profile is for. In the dropdown, select the App ID you created:select_app_id

Click Continue and you’ll need to select the Distribution Certificate to associate with the profile. Select the certificate you just created:
Screen Shot 2017-06-21 at 10.33.50 AM

Click Continue and you’ll need to select the devices that are allowed to run this application built with this profile. Select the devices you added or want to enable:Screen Shot 2017-06-21 at 10.34.00 AM

Click Continue and give your profile a Nameprofile_preview
Click Continue and you can review the profile before generating it.

Click Generate and then Download the profile. Double-Click or open the newly downloaded profile and it will then be instlled into XCode.

Now with your certificate installed on the machine’s keychain and the profile installed into XCode, you’ll be able to build against iOS devices in Release. Doing this build will then generate an .ipa file. This file is what can be uploaded to Mobile Center, HockeyApp, or any other distribution channel you want to use.

Next Steps

In the next few blog posts, we’ll look at actually uploading a new build .ipa file to both HockeyApp and Mobile Center so that test users can download and install it on their devices. Stay tuned!

If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.

Xamarin.Tip – Fixing the VS for Mac Xamarin.Forms Template Android Issues

Here’s another quick resolution to one of the most annoying issues I’ve faced in working with Visual Studio for Mac…

The Problem

When creating a brand new, out of the box, Xamarin.Forms project, it didn’t work. This has been a common trend for Xamarin/Microsoft over the last year or so as they roll out Xamarin + Visual Studio updates. The latest culprit was that the Android project was never able to actually install the Xamarin.Forms package from nuget. This was not obvious during the actual build, since the project DID load successfully, and the build errors were along the lines of not being able to find Android Support library resources in the styles.xml. This also caused an issue where source files were not loading with intellisense or highlighting, so the error was even less obvious (see Xamarin.Tip – Fixing the Highlighting Drop In Your Xamarin.Android Projects).

When going into the nuget package manager and trying to manually add the Xamarin.Forms package, I was hit with this error:

Could not install package 'Xamarin.Android.Support.v4 23.3.0'. You are trying to install this package into a project that targets 'MonoAndroid,Version=v2.3'
...

The Solution

My first instinct was to check the Android target version in the project options to make sure that they were within the supported SDK range, and they were:
Screen Shot 2017-06-21 at 4.05.17 PM

Unlike Visual Studio 2017 (or any Windows VS with Xamarin), there was no “Compile using Android Version” picker. THIS field is what is really determines the projects target framework level, but VS for Mac doesn’t have it like it should (hope you’re reading this Microsoft!).

Let’s fix it manually:

  1. Unload your Android project by right clicking it in the Solution explorer and clicking “Unload”
  2. Open the Android project .csproj in the editor by right clicking > Tools > Edit.
  3. For each build configuration, add an explicit TargetFrameworkVersion that’s compatible. In my case that was v4.4
  4. Your .csproj should look like this:
    <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    ... 
      <PropertyGroup>
        <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
        <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
        <ProjectGuid>{2BC9AAD5-D311-499E-8B06-A63CCB65C633}</ProjectGuid>
        <ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
        <OutputType>Library</OutputType>
        <RootNamespace>AppName.Droid</RootNamespace>
        <AssemblyName>AppName.Droid</AssemblyName>
        <AndroidApplication>True</AndroidApplication>
        <AndroidResgenFile>Resources\Resource.designer.cs</AndroidResgenFile>
        <AndroidResgenClass>Resource</AndroidResgenClass>
        <AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
        <MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
        <MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
        <AndroidUseLatestPlatformSdk>true</AndroidUseLatestPlatformSdk>
        <TargetFrameworkVersion>v4.4</TargetFrameworkVersion> <!-- THIS IS WHAT YOU NEED TO ADD OR CHANGE -->
      </PropertyGroup>
      <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
        <DebugSymbols>true</DebugSymbols>
        <DebugType>full</DebugType>
        <Optimize>false</Optimize>
        <OutputPath>bin\Debug</OutputPath>
        <DefineConstants>DEBUG;</DefineConstants>
        <ErrorReport>prompt</ErrorReport>
        <WarningLevel>4</WarningLevel>
        <AndroidLinkMode>None</AndroidLinkMode>
        <TargetFrameworkVersion>v4.4</TargetFrameworkVersion><!-- THIS IS WHAT YOU NEED TO ADD OR CHANGE -->
      </PropertyGroup>
      <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
        <DebugSymbols>true</DebugSymbols>
        <DebugType>pdbonly</DebugType>
        <Optimize>true</Optimize>
        <OutputPath>bin\Release</OutputPath>
        <ErrorReport>prompt</ErrorReport>
        <WarningLevel>4</WarningLevel>
        <AndroidManagedSymbols>true</AndroidManagedSymbols>
        <AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
        <TargetFrameworkVersion>v4.4</TargetFrameworkVersion><!-- THIS IS WHAT YOU NEED TO ADD OR CHANGE -->
      </PropertyGroup>
    
    ...
    </Project>
    
  5. Close the .csproj editor
  6. Reload the project by right clicking in the Solution Explorer and selecting Reload
  7. Retry adding the Xamarin.Forms nuget package to the project

Now you should be good to go. Did this not work for you? Leave a comment below and I’d be happy to help you fix this annoying issue as well!

If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.

Xamarin.Tip – Mvvm Light and Dependency Injection

Inversion of Control and Dependency Injection are some design principles that help make our applications more flexible and scalable. They both help us separate our implementations and make it easy to substitute drastic changes to our implemented data or business logic whether it be for writing unit tests or product improvement.

Xamarin is a platform where IoC and DI fit extremely well. I’ve talked about this concept a few other times in both my blogs and videos about the Onion Architecture in Xamarin as well as how to call Platform Specific code from a Portable Class Library. You can find those posts and videos here:

  1. Onionizing Xamarin Part 6
  2. [VIDEO] Xamarin.Tips: Calling Platform-Specific Code from a PCL (Dependency Injection)

In this post, I want to talk about using DI with Mvvm Light at a VERY basic level.

First, let’s define an interface for a service we might use:

IUserService.cs

public interface IUserService
{
    Task<User> GetCurrentUserAsync();
}

Now let’s create two different implementations. One that will be the service used in the application and the other that will be used for testing.

UserService.cs

public class UserService : IUserService
{
    // makes a call to a web api to get a user
    public async Task<User> GetCurrentUserAsync()
    {
        using (var client = new HttpClient())
        {
            var response = await client.GetAsync("https://mywebapi.mydomain/api/currentuser");
            var content = await response.Content.ReadAsStringAsync();
            return JsonConvert.DeserializeObject<User>(content);
        }
    }
}

TestUserServices.cs

public class TestUserService : IUserService
{
    public Task<User> GetCurrentUserAsync()
    {
        return Task.FromResult(new User { Name = "Test User" });
    }
}

Now we need a ViewModel that will use this service. We define a private readonly IUserService and then inject the implementation that we want in the constructor of the ViewModel.

CurrentUserViewModel.cs

public class CurrentUserViewModel : ViewModelBase
{
    // use the interface as the service and inject the implementation in the constructur
    private readonly IUserService _userService;
    private User _user;

    public User User
    {
        get
        {
            return _user;
        }
        set
        {
            Set(ref _user, value);
        }
    }

    public CurrentUserViewModel(IUserService userService)
    {
        _userService = userService;
    }

    public async Task UpdateUserAsync()
    {
        User = await _userService.GetCurrentUserAsync();
    }
}

Now let’s define an IoCConfig that handles registering dependencies and implementations.

IoCConfig.cs

public class IoCConfig
{
    public IoCConfig()
    {
        // use SimpleIoc from MvvmLight as our locator provider
        ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
    }

    // register the real implementation
    public void RegisterServices()
    {
        SimpleIoc.Default.Register<IUserService, UserService>();
    }

    // register the test implementation
    public void RegisterTestServices()
    {
        SimpleIoc.Default.Register<IUserService, TestUserService>();
    }

    // register the view model
    public void RegisterViewModels()
    {
        SimpleIoc.Default.Register<CurrentUserViewModel>();
    }
}

Now that we can register our Services as well as our ViewModels, the dependency resolver from SimpleIoc can retrieve an instance of CurrentUserViewModel with whichever version of IUserService is registered depending on whether we call RegisterServices or RegisterTestServices.

Now we can retrieve our instance of the CurrentUserViewModel by calling

var currentUserViewModel = ServiceLocator.Current.GetInstance<CurrentUserViewModel>();

MvvmLight recommends using a ViewModelLocator to get the instance of your ViewModels:

ViewModelLocator.cs

public class ViewModelLocator
{
    private readonly IoCConfig _iocConfig;
    public CurrentUserViewModel CurrentUser
    {
        get
        {
            return ServiceLocator.Current.GetInstance<CurrentUserViewModel>();
        }
    }

    public ViewModelLocator()
    {
        _iocConfig = new IoCConfig();
        _iocConfig.RegisterServices();
        //_iocConfig.RegisterTestServices();
        _iocConfig.RegisterViewModels();
    }

}

It’s recommended to either create your ViewModelLocator at the app start up, or if you’re using Xamarin.Forms, register it as a Resource in your App.xaml

<Application ...     xmlns:locator="clr-namespace:YOUR_LOCATOR_LOCATION">
    <Application.Resources>
        <ResourceDictionary>
            <locator:ViewModelLocator x:Key="Locator"/>
        </ResourceDictionary>
    </Application.Resources>
</Application>

Now in your XAML pages, you can automatically wire up your view model.

MainPage.xaml

<ContentPage ...     BindingContext="{Binding Source={StaticResource Locator}, Path=CurrentUser}"     Title="{Binding User.Name}">
...
</ContentPage>

In order to change to your testing data, you can just switch which call to your IoCConfig is made for registering your dependency without having to make any changes to any of your other layers or UI!

If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.

Unity.Tip – Create a Rotating Sun

Changing pace from the usual mobile app development, let’s talk about Unity!

Here’s a quick tip on adding a rotating sun to your Scenes to give your game a feeling of Day and Night.

First, add a light source or use the default light from a new scene, then Add a new Component .

Select Create new script and call it LightController. This script will have a public property for the length of the day you wish your sun to rotate, then handle the rotation speed calculation.

LightController.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class LightController : MonoBehaviour {
	public float DayLength;
	private float _rotationSpeed;

	void Update(){
		_rotationSpeed = Time.deltaTime / DayLength;
		transform.Rotate (0, _rotationSpeed, 0);
	}
}

Back in Unity, set your DayLength to a desired speed, and run your game.

Screen Shot 2017-05-30 at 5.02.35 PM
You’ll notice your sun/light moving around your scene and giving your area a sense of day and night.

If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.

Xamarin.Tip – Adding Dynamic Elevation to Your Xamarin.Forms Buttons

Before Reading

In a previous post, I talked about bringing Material Design to your iOS applications in Xamarin.Forms and adding drop shadows to them. You might want to read that here first: Xamarin.Tips – Creating a Material Design Button in iOS

In another post, we learned how to override the Android Button Elevations. We will be doing this in this post in order to set a dynamic elevation. You can read that here: Xamarin.Tips – Overriding Android Button Shadows/Elevation

Now in this post, we will combine these two concepts with a new custom Xamarin.Forms component called MaterialButton that will have a new Elevation property to control the elevation and shadow of the underlying button control.

The source code and an example can be found here: https://github.com/SuavePirate/MaterialButton

Using the existing code

You can of course use the code I wrote and put on GitHub for this. In order to use it, simply:

  1. Clone the repository at  https://github.com/SuavePirate/MaterialButton
  2. Include all 3 `src` projects in your Solution
  3. Reference the Shared project in all Xamarin.Forms and platform projects
  4. Reference the Android project in your Android projects
  5. Reference the iOS project in your iOS projects
  6. Use the control as below (see example projects to demo)

Reference the control in your XAML:

MainPage.xaml

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"              xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"              xmlns:local="clr-namespace:MaterialButtonExample"              xmlns:suave="clr-namespace:SuaveControls.MaterialButton.Shared;assembly=SuaveControls.MaterialButton.Shared"              x:Class="MaterialButtonExample.MainPage">

	<suave:MaterialButton x:Name="MyButton"                            BackgroundColor="#03A9F4"                            TextColor="White"                            Text="Click to raise elevation"                            Elevation="1"                            VerticalOptions="Center"                            HorizontalOptions="Center"                           WidthRequest="300"                           Clicked="MyButton_Clicked"/>

</ContentPage>

MainPage.xaml.cs

namespace MaterialButtonExample
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
        }

        private void MyButton_Clicked(object sender, EventArgs e)
        {
            MyButton.Elevation++;
        }
    }
}

In your iOS AppDelegate you’ll also need to call the Initialize method to ensure that the Custom renderer does not get excluded during linking:

AppDelegate.cs

namespace MaterialButtonExample.iOS
{
    [Register("AppDelegate")]
    public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
    {

        public override bool FinishedLaunching(UIApplication app, NSDictionary options)
        {
            global::Xamarin.Forms.Forms.Init();
            MaterialButtonRenderer.Initialize();
            LoadApplication(new App());

            return base.FinishedLaunching(app, options);
        }
    }
}

Now you can see your results!

Creating Your Own Material Design Button

First things first, let’s create our new Xamarin.Forms control before we implement our custom renderers:

MaterialButton.cs

namespace SuaveControls.MaterialButton.Shared
{
    public class MaterialButton : Button
    {
        public static BindableProperty ElevationProperty = BindableProperty.Create(nameof(Elevation), typeof(float), typeof(MaterialButton), 4.0f);

        public float Elevation
        {
            get
            {
                return (float)GetValue(ElevationProperty);
            }
            set
            {
                SetValue(ElevationProperty, value);
            }
        }
    }
}

Now let’s implement our iOS and Android custom renderers.

iOS:
MaterialButtonRenderer


[assembly: ExportRenderer(typeof(MaterialButton), typeof(MaterialButtonRenderer))]
namespace SuaveControls.MaterialButton.iOS
{
    public class MaterialButtonRenderer : ButtonRenderer
    {
        public static void Initialize()
        {
            // empty, but used for beating the linker
        }
        protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
        {
            base.OnElementChanged(e);

            if (e.NewElement == null)
                return;

        }

        public override void Draw(CGRect rect)
        {
            base.Draw(rect);
            UpdateShadow();
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
            if(e.PropertyName == "Elevation")
            {
                UpdateShadow();
            }
        }

        private void UpdateShadow()
        {

            var materialButton = (Shared.MaterialButton)Element;

            // Update shadow to match better material design standards of elevation
            Layer.ShadowRadius = materialButton.Elevation;
            Layer.ShadowColor = UIColor.Gray.CGColor;
            Layer.ShadowOffset = new CGSize(2, 2);
            Layer.ShadowOpacity = 0.80f;
            Layer.ShadowPath = UIBezierPath.FromRect(Layer.Bounds).CGPath;
            Layer.MasksToBounds = false;

        }
    }
}

Notice how we use the UpdateShadow method to use the Elevation property to set the ShadowRadius of our UIButton's Layer.

It’s important to also make the UpdateShadow call in the OnElementPropertyChanged so that we can dynamically change the Elevation property in our Xamarin.Forms control and see it reflected in the underlying UIButton.

Now let’s do it on Android:

MaterialButtonRenderer

[assembly: ExportRenderer(typeof(MaterialButton), typeof(MaterialButtonRenderer))]
namespace SuaveControls.MaterialButton.Droid
{
    public class MaterialButtonRenderer : Xamarin.Forms.Platform.Android.AppCompat.ButtonRenderer
    {
        ///
<summary>
        /// Set up the elevation from load
        /// </summary>

        /// <param name="e"></param>
        protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Button> e)
        {
            base.OnElementChanged(e);
            if (e.NewElement == null)
                return;

            var materialButton = (Shared.MaterialButton)Element;

            // we need to reset the StateListAnimator to override the setting of Elevation on touch down and release.
            Control.StateListAnimator = new Android.Animation.StateListAnimator();

            // set the elevation manually
            ViewCompat.SetElevation(this, materialButton.Elevation);
            ViewCompat.SetElevation(Control, materialButton.Elevation);
        }

        public override void Draw(Canvas canvas)
        {
            var materialButton = (Shared.MaterialButton)Element;
            Control.Elevation = materialButton.Elevation;
            base.Draw(canvas);
        }

        ///
<summary>
        /// Update the elevation when updated from Xamarin.Forms
        /// </summary>

        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
            if(e.PropertyName == "Elevation")
            {
                var materialButton = (Shared.MaterialButton)Element;
                ViewCompat.SetElevation(this, materialButton.Elevation);
                ViewCompat.SetElevation(Control, materialButton.Elevation);
                UpdateLayout();
            }
        }
    }
}

Just as mentioned in the iOS implementation, we need to make sure that we implement our changes in both the OnElementChanged method as well as the OnElementPropertyChanged event to ensure we are able to update our Elevation with MVVM.

Now we can use our control in our pages!

MainPage.xaml

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"              xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"              xmlns:local="clr-namespace:MaterialButtonExample"              xmlns:suave="clr-namespace:SuaveControls.MaterialButton.Shared;assembly=SuaveControls.MaterialButton.Shared"              x:Class="MaterialButtonExample.MainPage">

	<suave:MaterialButton x:Name="MyButton"                            BackgroundColor="#03A9F4"                            TextColor="White"                            Text="Click to raise elevation"                            Elevation="1"                            VerticalOptions="Center"                            HorizontalOptions="Center"                           WidthRequest="300"                           Clicked="MyButton_Clicked"/>

</ContentPage>

MainPage.xaml.cs

namespace MaterialButtonExample
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
        }

        private void MyButton_Clicked(object sender, EventArgs e)
        {
            MyButton.Elevation++;
        }
    }
}

In your iOS AppDelegate you’ll also need to call the Initialize method to ensure that the Custom renderer does not get excluded during linking:

AppDelegate.cs

namespace MaterialButtonExample.iOS
{
    [Register("AppDelegate")]
    public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
    {

        public override bool FinishedLaunching(UIApplication app, NSDictionary options)
        {
            global::Xamarin.Forms.Forms.Init();
            MaterialButtonRenderer.Initialize();
            LoadApplication(new App());

            return base.FinishedLaunching(app, options);
        }
    }
}

Now you can see your results!

If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.