0 Comments

When ever you want to display a list or collection of information under iOS tables are often the choice you will end up using. But what if you have content that changes and you want to update the data in a cell? Well that's what bindings are for right? But how do you use bindings in a UITableViewCell? Well let's check it out.

Welcome to our countdown app. As you can see basically it is a list of timers, that are displayed in a cell and updated as they are ticking down.

CellBinding

If we look at the basic setup of a displayed table, we will have UITableView which uses a UITableViewSource for managing the collection to be displayed and the rendering of the UITableViewCells. Then there is the view model which is providing a list of items, in our case timers, that should be displayed. So let's go through each of the items from View Model to Cell.

The View Model

MVVM Light comes with helpers that allow you to convert an ObservableCollection to a UITableViewSource. Choosing this option will further ensure ensure the UI is updated when we add or remove an element to our collection. So let's take it:

public class MainViewModel : ViewModelBase
{
    public MainViewModel()
    {
        // ...
        Countdowns = new ObservableCollection<CountdownViewItem>();
        Countdowns.Add(new CountdownViewItem(new TimeSpan(0, 13, 37)));
    }

    // ...
    public ObservableCollection<CountdownViewItem> Countdowns { get; private set; }

    // ...
}

The CountDownViewItem hold's the state of the individual item. Hence the timer logic is embedded into it and will also implement the RaisePropertyChanged.

public class CountdownViewItem : ViewModelBase
{
    DateTime _expirationTimestamp;

    public CountdownViewItem(TimeSpan timespan)
    {
        if (timespan == null) throw new ArgumentNullException(nameof(timespan));
        _expirationTimestamp = DateTime.UtcNow + timespan;
        Countdown();
    }

    string _remainingTimeString;
    public string RemainingTimeString
    {
        get
        {
            return _remainingTimeString;
        }
        set
        {
            if (value == _remainingTimeString) return;
            _remainingTimeString = value;
            RaisePropertyChanged(nameof(RemainingTimeString));
        }
    }

    private async void Countdown()
    {
        while (DateTime.UtcNow < _expirationTimestamp)
        {
            TimeSpan remainingTime = _expirationTimestamp - DateTime.UtcNow;
            RemainingTimeString = remainingTime.ToString(@"hh\:mm\:ss");
            await Task.Delay(millisecondsDelay: 490);
        }

        RemainingTimeString = "Timer Expired";
    }
}

So let's put these view models to use in the UI.

The Table View

One can choose to create the table either in a story board or in code. Choosing the code path we would have a UIViewController that creates the UITableView, set's the layout and creates the UITableSource from the view models collection:

class ViewController : UIViewController
{
    // private members

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();

        // Setup view and layouting

        // Setup bindings
        _tableViewController = Vm.Countdowns.GetController(CreatePersonCell, BindCellDelegate);
        _tableViewController.TableView = CountdownsTableView;

        AddTimerButton.SetCommand("TouchUpInside", Vm.AddCountdownCommand);
    }

    private void BindCellDelegate(UITableViewCell cell, CountdownViewItem countdownViewItem, NSIndexPath path)
    {
        var bindableCell = (CustomCell) cell;
        bindableCell.Configure(countdownViewItem);
    }

    private UITableViewCell CreatePersonCell(NSString cellIdentifier)
    {
        return CountdownsTableView.DequeueReusableCell(nameof(CustomCell));
    }
}

Note we are using a custom cell, and we are registering it to so we can reuse the cell. This will enable our app to reuse created cells I.e. improve the memory footprint, performance and is considered best practice. We will go into the details of that cell shortly.

If you are looking for the storyboard approach, note that this step and only this step will differ. Check out the GitHub repository for a sample.

The Cell

The cell is invoked when via the BindCellDelegate method. So we can populate our cell with binding like this:

public class CustomCell : UITableViewCell
{
    CountdownViewItem _countdownViewItem;
    // ui label and constructors

    private void InitCell()
    {
        // Layout cell
    }

    public Binding<string, string> _timerBinding;

    internal void Configure(CountdownViewItem countdownViewItem)
    {
        _countdownViewItem = countdownViewItem;
        _timerBinding = this.SetBinding(() => _countdownViewItem.RemainingTimeString, () => RemainingTimeLabel.Text);
    }
}

It is very important that you store the passed in view item as member variable in your method. If you do not do this, you will only see the initial value and the binding will never update the view!

Since we are reusing our cells we have to assume for one that our cells might have already been used by a previous item and that there might be an existing binding present.

Let's remember that Bindings boil down to events. And when we register an event handler we should always ensure that we are not creating a memory leak I.e. that we deregister the event handler. MVVM Light provides the method Detach which will deregister the event handler(s) behind the binding. Now why is this important? Well lists tend to come with multiple items. Having a memory leak for each item tends to be a bad design strategy to say the least. Therefor it is important to ensure that the binding in the cells are detached when it is no longer used.

This is also the reason why we are using a custom cell. It allows us to override the method PrepareForReuse which is invoked every time a cell is being reused. In this method we can restore the cell to it's initial state:

public override void PrepareForReuse()
{
    base.PrepareForReuse();
    _timerBinding?.Detach();
}

And that is how you can create bindings in a UITableViewCell.

Conclusion

In this blog post we saw how we can create a binding for a UITableViewCell. We also covered how to avoid memory leaks when reusing cells (and you should generally reuse cells). Generally do not use bindings for static content in cells as they might impact your performance. When ever possible do not change the layout (constraints) due to a change in the content. Though not supported by the UITableViewSource created by MVVM Light at the time of writing, try using different custom cells for different layouts.

You can find the entire app sample on GitHub.

0 Comments

Ever wondered how those slick screen transitions are made by those other apps? Using transitions do give an app that extra polish. But what's even more important it will give your app an edge when it comes to user experience (UX).

In this post we will implement the following transition:

CustomTransition

Before we dive into the code. Be aware that every transition follows the following steps::

  • Configure the transition on the destination View Controller
  • Implement the transition delegate
  • Implement the transition animation
  • If applicable implement the dismiss transition animation

You do not have to implement the dismiss transition. But if you navigate back to the originating page. You really should do this.

Configuring the custom transition

Short digression: There is no consensus in the community what the best approach is to write your UI. Some prefer to do it in code (such as myself) others prefer to use the storyboards. So we will just look at both of them. The good thing is that apart from the configuration the rest of the code is identical.

Configure custom transitions with code

The transition in iOS is actually defined on the target View Controller. In our example we will navigate to the next View Controller as soon as the user selects the button:

_button.TouchUpInside += (e, s) =>
{
    var vc = new ModalViewController(this)
    {
        ModalPresentationStyle = UIModalPresentationStyle.Custom,
        TransitioningDelegate = new GrowTransitioningDelegate(_button)
    };

    NavigationController.PresentViewController(vc, true, null);
};

Note that we configure the ModalPresentationStyle and TransitionDelegate on the view controller that we are navigating to. The GrowTransitionDelegate takes the originating UIView. If you are not using Storyboards you can skip the section and dive right into how the GrowTransitioningDelegate is implemented.

Configure custom transitions with Storyboards

If you are using storyboards you will be familiar with segues. To define a custom transition animation you will have to configure your segue as follows:

Configure the storyboard segue to use Present Modal and leave the Presentation and Transition to Default.

Then in the originating view controller you can override the PrepareForSegue method:

public override void PrepareForSegue(UIStoryboardSegue segue, NSObject sender)
{
    base.PrepareForSegue(segue, sender);

    var destinationVC = segue.DestinationViewController as SecondViewController;
    destinationVC.Callee = this;
    destinationVC.TransitioningDelegate = new GrowTransitioningDelegate(sender as UIView);
    destinationVC.ModalPresentationStyle = UIModalPresentationStyle.Custom;
}

The target view controller in our case is name SecondViewController. It looks fairly similar to the event handler we defined before. On the destination view controller we set the TransitioningDelegate to GrowTransitioningDelegate which takes the originating UIView as constructor parameter. The only difference is that we do no longer pass the instance of the calling view controller as constructor parameter but use a property named Callee on the SecondViewController.

Setting up the delegate

The GrowTransitionDelegate inherits from UIViewControllerTransitioningDelegate from which we can override methods to add a custom animation for presenting as follows:

public class GrowTransitioningDelegate : UIViewControllerTransitioningDelegate
{
    readonly UIView _animationOrigin;

    public GrowTransitioningDelegate(UIView animationOrigin)
    {
        _animationOrigin = animationOrigin;
    }

    public override IUIViewControllerAnimatedTransitioning GetAnimationControllerForPresentedController(UIViewController presented, UIViewController presenting, UIViewController source)
    {
        var customTransition = new GrowTransitionAnimator(_animationOrigin);
        return customTransition;
    }
}

Let's follow along the scenario of the user navigating to another page. The GetAnimationControllerForPresentedController provides an animation implementation that inherits from IUIViewControllerAnimatedTransitioning. The originating UIView, a button in this example, is passed to the animation in the constructor. And that is all the transitioning delegate has to implement. So let's see how we implement the actual animation.

Implementing the transition animation

The animations are defined in a class that inherit from UIViewControllerAnimatedTransitioning. The following two methods have to implemented:

public class GrowTransitionAnimator : UIViewControllerAnimatedTransitioning
{
    readonly UIView _animationOrigin;

    public GrowTransitionAnimator(UIView animationOrigin)
    {
        _animationOrigin = animationOrigin;
    }

    public override async void AnimateTransition(IUIViewControllerContextTransitioning transitionContext)
    {
        // The animation 
    }

    public override double TransitionDuration(IUIViewControllerContextTransitioning transitionContext)
    {
        return 0.3;
    }
}

The TransitionDuration method defines how long the animation will take. Usually this should be around 300 to 500 milliseconds. In the AnimateTransition method the actual transition and animation are defined.

The iOS SDK provides capabilities for creating animations such as transition animations. Using these will significantly reduce the effort required to create an animation to defining the initial and desired target state of a UI object. The rendering of the animation is performed by iOS on the GPU. So not only will your app look great it will also be snappy since GPUs eat transformations for breakfast Smile

The AnimateTransition method usually follows the following pattern:

  1. Get your source and destination view controller and view
  2. Get the animations container view and add the destination
  3. Define the animation states
    1. The final state of the target view
    2. The initial state of the target view
  4. Perform the animation and inform the transition context when it is completed

So let's go through this step by step. First let's get the source and destination view controllers and view:

public override async void AnimateTransition(IUIViewControllerContextTransitioning transitionContext)
{
    // Get the from and to View Controllers and their views
    var fromVC = transitionContext.GetViewControllerForKey(UITransitionContext.FromViewControllerKey);
    var fromView = fromVC.View;
    
    var toVC = transitionContext.GetViewControllerForKey(UITransitionContext.ToViewControllerKey);
    var toView = toVC.View;
     
    // ...
}

Then get the animation container and add the destination to it:

public override async void AnimateTransition(IUIViewControllerContextTransitioning transitionContext)
{
    // ...

    // Add the to view to the transition container view
    var containerView = transitionContext.ContainerView;
    containerView.AddSubview(toView);
        
    // ...
}

Now comes the part where we will define the animation. We want to start our animation in the middle of the button that the user selected I.e. which our animation class has received over the constructor.

public override async void AnimateTransition(IUIViewControllerContextTransitioning transitionContext)
{
    // ...

    // Set the desired target for the transition
    var appearedFrame = transitionContext.GetFinalFrameForViewController(toVC);
    
    // Set how the animation shall start
    var initialFrame = new CGRect(_animationOrigin.Frame.GetMidX(), _animationOrigin.Frame.GetMidY(), 0, 0);
    var finalFrame = appearedFrame;
    toView.Frame = initialFrame;
    
    // ...
}

Now that is left to do is execute the animation and await the result. The result is then used to signal to the transition context that the animation has completed:

public override async void AnimateTransition(IUIViewControllerContextTransitioning transitionContext)
{
    // ...

    var isAnimationCompleted = await UIView.AnimateAsync(TransitionDuration(transitionContext), () => {
        toView.Frame = finalFrame;
    });
    
    transitionContext.CompleteTransition(isAnimationCompleted);
}

And our transition animation is completed. If you are looking to implement a different animation. For instance one where the animation starts from a corner. Changing the origin point of the animation will provide you with the desired effect.

Side effects While animations are great and you will perhaps notice that some UI elements are drawn early and then move into position during the transition. This can feel slightly off. So while working with animations it can make sense to move the layout code of the UI components to the ViewDidAppear method.

Creating the dismiss animation

For having the inverse animation for dismissing the view. First the GetAnimationControllerForDismissedController method has to be overwritten in the delegate class (same one as before):

public class GrowTransitioningDelegate : UIViewControllerTransitioningDelegate
{
    readonly UIView _animationOrigin;

    public GrowTransitioningDelegate(UIView animationOrigin)
    {
        _animationOrigin = animationOrigin;
    }

    public override IUIViewControllerAnimatedTransitioning GetAnimationControllerForPresentedController(UIViewController presented, UIViewController presenting, UIViewController source)
    {
        // ...
    }

    public override IUIViewControllerAnimatedTransitioning GetAnimationControllerForDismissedController(UIViewController dismissed)
    {
        var customTransition = new ShrinkTransitionAnimator(_animationOrigin);
        return customTransition;
    }
}

Then create an implementation which inherits UIViewControllerAnimatedTransitioning and has the desired reverse animation.

public class ShrinkTransitionAnimator : UIViewControllerAnimatedTransitioning
{
    readonly UIView _animationOrigin;

    public ShrinkTransitionAnimator(UIView animationTarget)
    {
        _animationOrigin = animationTarget;
    }

    public override async void AnimateTransition(IUIViewControllerContextTransitioning transitionContext)
    {
        // Get the from and to View Controllers and their views
        var fromVC = transitionContext.GetViewControllerForKey(UITransitionContext.FromViewControllerKey);
        var fromView = fromVC.View;

        var toVC = transitionContext.GetViewControllerForKey(UITransitionContext.ToViewControllerKey);
        var toView = toVC.View;

        // Add the to view to the transition container view
        var containerView = transitionContext.ContainerView;

        // Set the desired target for the transition
        var appearedFrame = transitionContext.GetFinalFrameForViewController(fromVC);

        // Set how the animation shall end
        var finalFrame = new CGRect(_animationOrigin.Frame.GetMidX(), _animationOrigin.Frame.GetMidY(), 0, 0);
        fromView.Frame = appearedFrame;

        var isAnimationCompleted = await UIView.AnimateAsync(TransitionDuration(transitionContext), () => {
            fromView.Frame = finalFrame;
        });

        fromView.RemoveFromSuperview();

        transitionContext.CompleteTransition(isAnimationCompleted);
    }

    public override double TransitionDuration(IUIViewControllerContextTransitioning transitionContext)
    {
        return 0.3;
    }
}

Though looking similar there are a few key points. For one the origin this time is the view controller that we want to dismiss. Since it is already part of the View tree it no longer has to be added. Quite the contrary you will actually want to remove it once the animation is completed.

Conclusion

In this post we went through the steps required to create a custom transition. Further we looked at how we can implement the dismiss animation. The steps can be reused for different kind of animations which are defined in the implementation of the UIViewControllerAnimatedTransitioning class.

You can find the complete code sample on GitHub.

0 Comments

Just stumbled over this one in a recent endeavour. The default behaviour of iOS is to use the title of the previous navigation controller as the text for the back button.

BackButtonNotSet

If you want a different text such as back you might find this recipe from Xamarin which works. But it does feel like a hack and it requires you to adopt this approach in every view.

Another approach is to set the NavigationItem.BackBarButtonItem attribute in the view controller:

NavigationItem.BackBarButtonItem = new UIBarButtonItem {Title = "Back"};

The set text will be displayed as the back button of the next view controller. In other words previous view controller always sets the back button text of the following.

BackButtonSet

If you are using a central point to generate your views e.g. a navigation service or factory you can apply this to every view you create. Which makes this approach a lot less error prone.

If you are using storyboards you have a further option. The title can be set directly in the designer under Navigation Item, Back Button.

Set back button title in Storyboard editor

Note this option is only available when you choose the (no longer recommended) push navigation for your segue.

Conclusion

In this post we saw how we can set the title of the back button. The title of the back button is always set in the preceding view controller.

Using a storyboard the title can be set directly in the storyboard designer.

You can find an entire little app sample on GitHub.

0 Comments

Traffic lights at the crossroads

In this post we look at getting notified when the user chooses to navigate back. If you have to be in control of your navigation under iOS I strongly recommend you check out using a modal navigation. But what if you simply want to track when the navigation hits the back button. Or uses the swipe gesture to go back to a previous page.

While there is no direct event, there a few ways to achieve this.

Custom NavigationController

One way is to subclass the UINavigationController:

public class AwareNavigationController : UINavigationController
{
    public event EventHandler PoppedViewController;

    public AwareNavigationController() : base() { }
    public AwareNavigationController(UIViewController rootViewController) : base(rootViewController) { }
    public AwareNavigationController(IntPtr intPtr) : base(intPtr) { }
    public AwareNavigationController(NSCoder coder) : base(coder) { }
    public AwareNavigationController(NSObjectFlag t) : base(t) { }
    public AwareNavigationController(string nibName, NSBundle bundle) : base(nibName, bundle) { }
    public AwareNavigationController(Type navigationBarType, Type toolbarType) : base(navigationBarType, toolbarType) { }

    public override UIViewController PopViewController(bool animated)
    {
        PoppedViewController?.Invoke(this, null);
        return base.PopViewController(animated);
    }
}

You can then start using the new navigation controller:

new AwareNavigationController(new ContainterViewController());

And hook up to it’s event:

public override void ViewWillAppear(bool animated)
{
    base.ViewWillAppear(animated);
    ((AwareNavigationController)NavigationController).PoppedViewController += ViewControllerPopped;
}


private void ViewControllerPopped(object sender, EventArgs e)
{
    Console.WriteLine("Going back Shell");
}

Use this approach if you want to register the navigation at a single point and provide this information as an event or even message. If you are writing a navigation service this approach will most probably be your best choice.

A couple of side notes when using this approach. Make sure to deregister the event handler. You will want to deregister the event handler in the ViewWillDisappear method. If you try to do this at a later stage I.e. ViewDidDisappear the navigation controller reference will already be null.

WillMoveToParentViewController

Alternatively to creating a custom navigation view controller you can override the following method in your ViewController:

public override void WillMoveToParentViewController(UIViewController parent)
{
    base.WillMoveToParentViewController(parent);
    if(parent == null) Console.WriteLine("Going back Shell");
}

If the passed in parent view controller is null, the view is being removed from the view stack. Note that if you have child view controllers you will have to extend the method above or the children will not be notified:

public override void WillMoveToParentViewController(UIViewController parent)
{
    base.WillMoveToParentViewController(parent);

    if (parent == null)
    {
        var childVCs = ChildViewControllers;
        foreach(var childVC in childVCs)
        {
            childVC.RemoveFromParentViewController();
        }

        Console.WriteLine("Method override: Going back Shell");
    }
}

I would recommend this approach when you are only need to detect the navigation in one or two ViewController's I.e. but do not require this event globally in the app.

Not recommended approach

If you have stumbled over the suggestion to simply check the attribute IsMovingFromParentViewController in the ViewWillDisappear / ViewDidDisappear, be aware that this is not recommended. While it might work at first, as soon as you need to detect the back navigation in a child view controller this attribute will always be set to true. Even if navigating to another view controller and not backwards.

public override void ViewDidDisappear(bool animated)
{
    base.ViewWillDisappear(animated);
    // not recommended!
    if(IsMovingFromParentViewController) Console.WriteLine("Going back Shell");
}

Since one can’t forbid a ViewController to be used as child view controller, I would strongly recommend to stay away from this approach.

Conclusion

In this post the different ways of getting notified of a back navigation in progress have been shown. If you have to block the user from navigating back I strongly recommend you use a modal navigation in the first place.

Find a small sample app on GitHub.

Know another option not mentioned here? Or have any thoughts on the approaches. Let me know by posting a comment.

0 Comments

pexels-photo-316465


When developing an app your design might require to use a font that is not available from iOS out of the box. So let’s see how a custom font can be added to ones app and how to use it in a Storyboard or Code.

Adding the font

Assuming you already have the font, note that iOS supports fonts that are stored in TTF or OTF formats. Custom fonts are copied into the Resource folder of your iOS project.

image of ios project, showing the custom font file in a subfolder named fonts in the resoucres folder

Ensure that the properties of the font file are set to BundleResource.

Creating the Fonts subfolder is optional. But since the Resources projects tends to collect a couple of items in larger projects. Let’s tidy things up from the start Smile

To use the font we will have to update the Info.plist file with the following lines:

image

Note: At the time of writing in Visual Studio 15.5.4 you will have to open the Info.plist file in an XML Editor. For this right click the file and select “Open with…” then choose the XML (Text) Editor. In Visual Studio for Mac the GUI Info.plist editor supports editing the source directly.

Using custom fonts in Storyboards

After adding for example a UILabel to the storyboard, select it. In the options click on the font and choose the custom font.

Storyboard

Using custom fonts in Code

In code behind using a custom font is pretty straight forward. For example in a label we can set the Font attribute as follows:

image

The result when running app with the combined storyboard and new label is:

Custom Font Screenshot

Conclusion

In this blogpost we went through the steps that have to be taken to add a custom font. Note that fonts are loaded during the start up of the app and if you go bonkers with them you might notice some performance impact.

You can find a small sample on GitHub. For setting the constraints in the code defined UI PureLayout.Net was used.