redth-org/BTProgressHUD

HUD only shows half a second on Dismiss() and not before

Closed this issue · 14 comments

Hi, I just starting using Xamarin.iOS and BTProgressHUD.

I understood while looking at older issues that I couldn't call it in viewDidLoad so I tried to prevent doing that error.

Here's my HomeViewController, look for the NOTE in GetPinsAndShowLoading. Why is this happening?

I'll follow up with a gif of what's happening so you can clearly get what's up.

Also, can't I start fetching stuff from my API in ViewDidLoad so it's faster (not waiting for the view to draw) and show your HUD anyways? I guess I could add a static variable to define wether or not it should display your HUD and play with that maybe?

Thanks for checking this out and keep up the good work!

public partial class HomeViewController : UIViewController
{
    #region Properties

    public List<Pin> Pins { get; set; }

    private API API { get; set; }

    private UITableViewController TableViewController { get; set; }

    public delegate bool DoneLoading();
    public DoneLoading OnCallBack { get; set; }

    #endregion

    #region Constructors

    public HomeViewController() : base("HomeViewController", null)
    {
        Title = NSBundle.MainBundle.LocalizedString("Home", "Home");
        TabBarItem.Image = UIImage.FromBundle("first");

        this.API = new API();

        this.TableViewController = new UITableViewController(UITableViewStyle.Plain);
    }

    #endregion

    #region Events

    public override void DidReceiveMemoryWarning()
    {
        // Releases the view if it doesn't have a superview.
        base.DidReceiveMemoryWarning();
    }

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

        UIRefreshControl refreshControl = new UIRefreshControl();

        refreshControl.ValueChanged += (object sender, EventArgs e) => {
            GetPinsAndShowLoading();
        };

        // adding table view
        TableViewController.TableView = new UITableView(View.Bounds);

        TableViewController.RefreshControl = refreshControl;

        // add subview to view
        Add(TableViewController.TableView);
    }

    public override void viewDidAppear(bool animated)
    {
                // Called in ViewDidAppear as instructed.
        GetPinsAndShowLoading();
    }

    #endregion

    /// <summary>
    /// Simply shows a loading sign and calls API for pins.
    /// </summary>
    public void GetPinsAndShowLoading()
    {
        // NOTE: It's supposed to show the loading now but it only flickers once it hit the .Dismiss();
        // So let's say it loads during 5 seconds, we'll see the HUD only half a second when calling .Dismiss();
        // Why is this happening?
        BTProgressHUD.Show("Loading...");

        // fetching pins and passing callback
        API.GetPins(GetPinsCallback);
    }

    /// <summary>
    /// Callback called once GetPins is done.
    /// </summary>
    /// <param name="pins">List<Pin></param>
    public void GetPinsCallback(List<Pin> pins)
    {
        Pins = pins;

        // use pins as source
        TableViewController.TableView.Source = new PinTableSource(Pins);
        BTProgressHUD.Dismiss();
    }
}

Here's the GIFs, just noticed (it happens fast). the .Show() is called when the GIF starts and the .Dismiss() is called when you see the Loading... sign blinking.

2013-11-21 15_02_14 2013-11-21 15_02_40

Hope that helps.

@tbergeron show has a default timeout of 1 second, which may be shorter if you are doing stuff on the main thread - the the 1 second is 750ms of your stuff, and 250 of showing the HUD

I have just made a quick change in this commit which should allow you to pass in a longer timer. However, you will need to call

ProgressHUD.Shared.Show(...)

not

BTProgressHUD.Show(...)

as it's only on the underlying class, not the static wrapper (does the same thing tho). You might want to make sure that API.GetPins(..) is working using a thread, not the main UI thread :)

Oh, and here's the checkin with that change

2c13af2

Alright, awesome! I was using the component from the Xamarin Component Store but I guess it won't be updated there yet. Would you mind telling how to use your component without the component store?

Can I simply open your solution and build it down to a DLL that I'll use within my solution? That's what I'm going to try.

Thanks a lot!

@tbergeron yup, thats al I do, Just grab the code down (git pull https://xxxx) and build it. It should - better! - work out of the box.

That did the trick! Also about the API.GetPins, you are right I'm using the main UI thread. That's something I'm going to have to learn to do hehe

Here's one last question, in the following example: How could I show a loading sign WHILE the interface is loading. If I put the .Show() before the .MakeKeyAndVisible() the sign just won't appear but if I put it after it will. I guess that's something else that I would have to separate from the current thread.

public void LoadMainUI() {
    ProgressHUD.Shared.Show("Loading...", -1, ProgressHUD.MaskType.Black, 0.75);

    tabBarController = new UITabBarController();
    tabBarController.ViewControllers = new UIViewController []
    {
        new HomeViewController(),
        new ProfileViewController()
    };

    window.RootViewController = tabBarController;
    window.MakeKeyAndVisible();
}

Any insight about that? Thanks a lot, you've been of great help!

I've not had to do that. I tend to load the minimum of stuff up front, so the UI load time hasn't been an issue.

And getting things of the main thread is fairly easy. Either you could

var pins = await API.GetPins();

and make GetPins async (see here: https://gist.github.com/nicwise/7026601) or just use Task.StartNew(() => { you code, then call the callback}); in GetPins.

I'd prefer the async/await version, personally. It's VERY clean. I'd need to check the syntax, but it might be as easy as:

public async Task<List<Pin>> GetPins() { return Task.StartNew(() => { your code return new List<Pin>(); //with your data in, obviously }); }

then your method becomes

BTProgressHUD.Show("Loading..."); var pins = await API.GetPins(GetPinsCallback); TableViewController.TableView.Source = new PinTableSource(pins); BTProgressHUD.Dismiss();
Nice and simple :)

You're the coolest man on earth! Thanks for this, I love to learn by reading code and that's actually pretty straight-forward. That's indeed pretty clean!

I'm going to really miss async/await when I'm in ObjectiveC land....

I'm sure of it! I guess you've been using MonoTouch/Xamarin for a while, any drawbacks or thing you miss when not using straight Objective-C? Just curious as I discovered major negatives with a similar experiment with Titanium. Nothing to worry about though Xamarin is just perfect so far it nails every little aspect I was worried about. Thanks again!

I've been using C# since v1beta, so I'm very used to it. Thats the main thing I'll miss - all the bits of System.*, generics, Tasks, async/await etc. UIKit and Cocoa are pretty complete - and you're using them in X.iOS already - but some of the other stuff is way nicer in C#.

I think the same, I've worked a few years for a C# only place and it was a blast. Now working with .NET classes again is a breeze. I also work with them in Unity3D so it's fun to use them almost everywhere lately 👍

While re-implementing my GetPins method with async, I did like you mentioned with StartNew so my new method looks like:

public async Task<List<Pin>> GetPins() {
    return Task.Factory.StartNew(() => {
        return new List<Pin>();
    }
}

and I keep getting this even after cleaning:
screen shot 2013-11-26 at 5 00 11 pm

I tried looking around online but every example I seem to encounter looks like mine. Any idea?

Oh I was doing the opposite! I had to put the async keyword on the method I wanted to use await in! That's where I goofed. Everything is back working now.

yeah, that got me the first few times