tip

A faster way to add image assets to your Xamarin.iOS project in Visual Studio 2017

A faster way to add image assets to your Xamarin.iOS project in Visual Studio 2017

While Visual Studio has an editor that shall help to add new image assets to the project, it is pretty slow when adding a bunch of new images. If you have to add a lot of images to add, it even crashes once in while, which can be annoying. Because I was in the process of adding more than a hundred images for porting my first app ever from Windows Phone to iOS and Android, I searched for a faster way – and found it.

What’s going on under the hood?

When you add an imageset to your assets structure, Visual Studio does quite some work. These are the steps that are done:

  1. creation of a folder for the imageset in the Assets folder
  2. creation of a Contents.jsonfile
  3. modification of the Contents.json file
  4. modification of the .csproj file

This takes some time for every image, and Visual Studio seems to be quite busy with these steps. After analyzing the way imagesets get added, I immediately recognized that I am faster than Visual Studio if I do that work manually.

How to add the assets – step by step

  1. right click on the project in Solution Explorer and select ‘Open Folder in File Explorer‘ and find the ‘Assets‘ folder  open_folder_in_file_explorer
  2. create a new folder in this format: ‘[yourassetname].imageset
  3. add your image to the folder
  4. create a new file with the name Contents.json   add_image_and_contents_file
  5. open the file (I use Notepad++ for such operations) and add this minimum required jsonto it:
    {
      "images": [
        {
          "scale": "1x",
          "idiom": "universal",
          "filename": "[yourimage].png"
        },
        {
          "scale": "2x",
          "idiom": "universal",
          "filename": "[yourimage].png"
        },
        {
          "scale": "3x",
          "idiom": "universal",
          "filename": "[yourimage].png"
        },
        {
          "idiom": "universal"
        }
      ],
      "properties": {
        "template-rendering-intent": ""
      },
      "info": {
        "version": 1,
        "author": ""
      }
    }
  6. go back to Visual Studio, right click on the project again and select ‘Unload Projectunload_project
  7. right click again and select ‘Edit [yourprojectname].iOS.csproj‘ edit_ios_csproj
  8. find the ItemGroup with the Assets
  9. inside the ItemGroup, add your imageset with these two entries:
    <ImageAsset Include="Assets.xcassets\[yourassetname].imageset\[yourimage].png">
      <Visible>false</Visible>
    </ImageAsset>
    <ImageAsset Include="Assets.xcassets\[yourassetname].imageset\Contents.json">
      <Visible>false</Visible>
    </ImageAsset>   
    
  10. close the file and reload the project by selecting it from the context menu in Solution Explorer

If you followed this steps, your assets should be visible immediately:
assets_with_added_imagesets

I did not measure the time exactly, but I felt I was significantly faster by adding all those images that way. Your mileage may vary, depending on the power of your dev machine.

Conclusion

Features like the AssetManager for iOS in Visual Studio are nice to have, but some have some serious performance problems (most of them are already known). By taking over the process manually, one can be a lot faster than a built-in feature. That said, like always, I hope this post is helpful for some of you.

Happy coding, everyone!

Posted by msicc in Dev Stories, iOS, Xamarin, 1 comment

Prevent accidentally exit of your Windows Phone SL app (with Telerik RadMessageBox or Toolkit CustomMessageBox)

preventExit

After finally being able to use my dev center account again after three long weeks, I am back into UniShare development. One of the most requested fixes was to not exit the app when users are not at the compose pivot.

Of course I am listening and I found a solution that should fit nearly all possible scenarios. Here is what I did:

        //handle back key press to prevent accidentally exit of the app
        protected async override void OnBackKeyPress(CancelEventArgs e)
        {
            //get the base handler
            base.OnBackKeyPress(e);

            //go back to the first pivot on all other pivots
            if (MainPivot.SelectedIndex != 0)
            {
                MainPivot.SelectedIndex = 0;
                e.Cancel = true;
            }
            //handle app exit
            else if (MainPivot.SelectedIndex == 0)
            {
                //cancel the back button press
                e.Cancel = true;

                //check if bool isExitQuestionCheckBoxChecked is false and the message needs to be displayed
                if (isExitQuestionCheckBoxChecked == false)
                {
                    //show the message box
                    MessageBoxClosedEventArgs args = await RadMessageBox.ShowAsync("Do you really want to exit the app?", MessageBoxButtons.YesNo, null, "don't ask me again, just exit next time", false, true);
                    if (args.ClickedButton == null)
                    {
                        //save check box value (use IsolatedStorage on earlier versions of Windows Phone)
                        App.SettingsStore.isExitQuestionCheckBoxChecked = isExitQuestionCheckBoxChecked;
                        //go back to the app
                        return;
                        
                    }
                    if (args.ButtonIndex == 0)
                    {
                        //set the check box value
                        isExitQuestionCheckBoxChecked = args.IsCheckBoxChecked;
                        //save check box value (use IsolatedStorage on earlier versions of Windows Phone)
                        App.SettingsStore.isExitQuestionCheckBoxChecked = isExitQuestionCheckBoxChecked;
                        //exit the app
                        Application.Current.Terminate();
                        
                    }
                    if (args.ButtonIndex == 1)
                    {
                        //set the check box value
                        isExitQuestionCheckBoxChecked = args.IsCheckBoxChecked;
                        //save check box value (use IsolatedStorage on earlier versions of Windows Phone)
                        App.SettingsStore.isExitQuestionCheckBoxChecked = isExitQuestionCheckBoxChecked;
                        //go back to the app
                        return;
                    }
                }
                //check if bool isExitQuestionCheckBoxChecked is false and the app should be exited
                else if (isExitQuestionCheckBoxChecked == true)
                {
                    Application.Current.Terminate();
                }
            }
        }

 

Let me explain. The first thing I am checking is if the current PivotItem is the one I want to display the message. If not, I am moving the Pivot to it.

The next step is to show the MessageBox and handle the buttons and the CheckBox. If no button is pressed, the codes saves the value false and goes back to the app.

If the Yes-button is pressed, the code saved the value of the CheckBox and exits. Same happens for the No-button.

If the user presses the back button the next time, the isExitQuestionCheckBoxChecked boolean gets checked – if the user does not want the message and checked it, the app exits as expected.

The above snippet uses the RadMessageBox.

When we use Toolkit’s CustomMessageBox, we need a slightly different approach. First, we do not override the OnBackKeyPress event – instead, we declare a new BackKeyPress event in the page’s constructor:

public MainPage()
{
    InitializeComponent();     
    BackKeyPress += MainPage_BackKeyPress;
}

 

Then, add this code to the newly generated event handler:

        //handle back key press to prevent accidentally exit of the app
        void MainPage_BackKeyPress(object sender, System.ComponentModel.CancelEventArgs e)
        {
            //go back to the first pivot on all other pivots
            if (MainPivot.SelectedIndex != 0)
            {
                MainPivot.SelectedIndex = 0;
                e.Cancel = true;
            }
            //handle app exit
            else if (MainPivot.SelectedIndex == 0)
            {   
                ////check if bool isExitQuestionCheckBoxChecked is false and the message needs to be displayed
                if (isExitQuestionCheckBoxChecked == false)
                {
                //generate a CheckBox as Content
                CheckBox chkbox = new CheckBox()
                {
                    Content = "don't ask me again, just exit next time",
                    Margin = new Thickness(0, 14, 0, -2)
                };

                //generate msg and handle the result
                CustomMessageBox msg = new CustomMessageBox()
                {
                    Title = "Attention",
                    Message = "Do you really want to exit the app?",
                    Content = chkbox,
                    LeftButtonContent = "yes",
                    RightButtonContent = "no"
                    
                };

                msg.Dismissed += (s1, e1) =>
                    {
                        switch (e1.Result)
                        {
                            case CustomMessageBoxResult.LeftButton:
                                //save check box value
                                isExitQuestionCheckBoxChecked = (bool)chkbox.IsChecked;

                                //save the check box value
                                if (IsolatedStorageSettings.ApplicationSettings.Contains("isExitQuestionCheckBoxChecked"))
                                {
                                    IsolatedStorageSettings.ApplicationSettings.Remove("isExitQuestionCheckBoxChecked");
                                }
                                IsolatedStorageSettings.ApplicationSettings.Add("isExitQuestionCheckBoxChecked", isExitQuestionCheckBoxChecked);

                                IsolatedStorageSettings.ApplicationSettings.Save();

                                //exit the app
                                Application.Current.Terminate();
                                break;
                            case CustomMessageBoxResult.RightButton:
                                //save check box value
                                isExitQuestionCheckBoxChecked = (bool)chkbox.IsChecked;

                                //save the check box value
                                if (IsolatedStorageSettings.ApplicationSettings.Contains("isExitQuestionCheckBoxChecked"))
                                {
                                    IsolatedStorageSettings.ApplicationSettings.Remove("isExitQuestionCheckBoxChecked");
                                }
                                IsolatedStorageSettings.ApplicationSettings.Add("isExitQuestionCheckBoxChecked", isExitQuestionCheckBoxChecked);

                                IsolatedStorageSettings.ApplicationSettings.Save();

                                //go back to the app
                                break;
                            case CustomMessageBoxResult.None:
                                break;
                            default:
                                break;
                        }
                    };
                    //show message
                    msg.Show();

                    //cancel all BackKey events
                    e.Cancel = true;

                }
                //check if bool isExitQuestionCheckBoxChecked is false and the app should be exited
                else if (isExitQuestionCheckBoxChecked == true)
                {
                    Application.Current.Terminate();
                }
            }
        }

 

This code is taken from a Windows Phone 8 project and does the same as the first snippet by using the Windows Phone Toolkit.

As always, I hope this is helpful for some of you.

Happy coding, everyone!

Posted by msicc in Dev Stories, windev, 0 comments