例としてXamarinを使用したモバイルアプリケーションで欠落しているコンテンツの画面

すべての始まり

多くの場合、エンタープライズアプリケーションを使用する場合、バックエンドからデータを受信せずに、画面にリストを表示するだけで、このような画面を処理する必要があります。





これは通常、設計者がまったく不在であり、すべてのウォーフレームが顧客からのスケッチの形式で提供されるか、設計者がいるという事実によるものですが、この要素は顧客にとってそれほど重要ではないと見なされ、単に無視されます。





, , . , .





-, . -, UI- - .





public class EmptyStateViewModel : ViewModel
{
    public EmptyStateViewModel(string image, string title, string description)
    {
        Image = image;
        Title = title;
        Description = description;
    }

    public string Image { get; }

    public string Title { get; }

    public string Description { get; }
}
      
      



( xaml Xamarin Forms) Bindings , , mvvm- .





?

- , - . - EmptyStateView, Retry, . EmptyStateViewModel, , .





public class ErrorStateViewModel : EmptyStateViewModel
{
    public ErrorStateViewModel(string image, string title, string description, string actionTitle, Command actionCommand)
        : base(image, title, description)
    {
        ActionTitle = actionTitle;
        ActionCommand = actionCommand;
    }

    public string ActionTitle { get; }

    public Command ActionCommand { get; }
}
      
      



?

- . . , -. None, null.





public static class OverlayFactory
{
    public static T None<T>()
        where T : EmptyStateViewModel
    {
        return null;
    }

    public static EmptyStateViewModel CreateCustom(string image, string title, string description)
    {
        return new EmptyStateViewModel(image, title, description);
    }

    public static ErrorStateViewModel CreateCustom(string image, string title, string description, string actionTitle, Command actionCommand)
    {
        return new ErrorStateViewModel(image, title, description, actionTitle, actionCommand);
    }
}
      
      



- -, , ,





public class SomeViewModel : BaseViewModel
{
    private IItemsLoadingService _itemsLoadingService;
    
    public SomeViewModel(IItemsLoadingService itemsLoadingService)
    {
        _itemsLoadingService = itemsLoadingService;
    }

    public ObservableCollection<ItemViewModel> Items { get; } = new ObservableCollection<ItemViewModel>();

    public EmptyStateViewModel EmptyState { get; protected set; }

    public ErrorStateViewModel ErrorState { get; protected set; }

    public override async Task InitializeAsync()
    {
        await base.InitializeAsync();

        await LoadItemsAsync();
    }

    private async Task LoadItemsAsync()
    {
        try
        {
            var result = await _itemsLoadingService.GetItemsAsync();
            var items = result.ToList();

            ErrorState = OverlayFactory.None<ErrorStateViewModel>();

            if (items.Count == 0)
            {
                EmptyState = OverlayFactory.CreateCustom("img_empty_state", "Title", "Description");
            }
            else
            {
                EmptyState = OverlayFactory.None<ErrorStateViewModel>();
                // Add items to list
            }
        }
        catch
        {
            ErrorState = OverlayFactory.CreateCustom("img_error_state", "Title", "Description", "Retry", new Command(() => LoadItemsAsync));
        }
    }
}
      
      



Binding EmptyState/ErrorState , mvvm-, , EmptyStateViewModel/ErrorStateViewModel null, . SetViewModel.





, View ViewModel View ViewState . ViewModel null - ViewState Gone, - Visible:





public void SetViewModel(EmptyStateViewModel viewModel)
{
    ViewModel = viewModel;

    View.Visibility = viewModel != null ? ViewStates.Visible : ViewStates.Gone;
}
      
      



iOS - constraints , - . enum, Android.





public void SetViewModel(EmptyStateViewModel viewModel)
{
    ViewModel = viewModel;

    View.SetVisibility(viewModel != null ? ViewStates.Visible : ViewStates.Gone);
}
      
      



extension





public static void SetVisibility(this UIView view, ViewVisibility visibility)
{
    var constraints = GetViewConstraints(view) ?? new NSLayoutConstraint[] {};

    if (visibility == ViewVisibility.Gone)
    {
        SaveViewConstraints(view, constraints);
        NSLayoutConstraint.DeactivateConstraints(constraints);
        view.Hidden = true;
        return;
    }
  
    if (visibility == ViewVisibility.Visible)
    {
        SaveViewConstraints(view, null);
        NSLayoutConstraint.ActivateConstraints(constraints);
        view.Hidden = false;
        return;
    }
}
      
      



ここで、ViewVisibility.Goneを設定する場合、ビューの制約を事前に保存して非アクティブ化します。逆に、可視性をオンにすると、以前に保存した制約を取得し、保存をリセットしてからアクティブにします。





private static NSLayoutConstraint[] GetViewConstraints(UIView view)
{
    return view.GetAssociatedObject<NSMutableArray<NSLayoutConstraint>>(Key)?.ToArray() ??
           view.Superview?.Constraints
               .Where(constraint => (constraint.FirstItem?.Equals(view) == true) || constraint.SecondItem.Equals(view))
               .ToArray();
}

private static void SaveViewConstraints(UIView view, NSLayoutConstraint[] constraints)
{
    NSMutableArray<NSLayoutConstraint> viewConstraints = null;

    if (constraints.Length > 0)
    {
        viewConstraints = new NSMutableArray<NSLayoutConstraint>();
        viewConstraints.AddObjects(constraints);
    }

    view.SetAssociatedObject(Key, viewConstraints, AssociationPolicy.RetainNonAtomic);
}
      
      



最初の方法では、以前に保存した制約がある場合はそれを取得でき、そうでない場合は現在の制約を取得できます。親ビューがない場合は、nullが返されます。





2番目の方法では、現在の制約を保存して、後で復元できるようにします。





したがって、データが欠落しているより快適な画面、またはエラーステータス画面を作成することが判明しました。





PS-ハブレに関する最初の記事なので、厳密に判断しないでください。しかし、どこかから始める必要があります。








All Articles