This was a simple app that lets the user scan a bar-code, take a photo then upload both the photo and bar-code to a website, it was an interesting first app.

Coming from the world of Xamarin iOS development there was a bit of culture shock with Activities and Fragments compared to view controllers. Plus if you want to access a form control you have to find it. Every control ends up as a constant in a large compiler generated file Resource.Designer.cs and to use objects mapped to them you need code like this. The controls objects are declared elsewhere.

            
            ScanButton = FindViewById<Button>(Resource.Id.barcodeButton);
            Barcode = FindViewById<TextView>(Resource.Id.barcode);
            StatusView = FindViewById<TextView>(Resource.Id.status);
            ImageView = FindViewById<ImageView>(Resource.Id.imageView1);
            PhotoButton = FindViewById<Button>(Resource.Id.photoButton);
            UploadButton = FindViewById<Button>(Resource.Id.uploadButton);
            SettingsButton = FindViewById<ImageButton>(Resource.Id.settingsButton);

Scanning Barcodes

The first big problem was bar-code scanning but the excellent open source Apache Licensed Zxing mobile barcode scanner component solved that. Tips for working with that, get a 5″ screen phone and scan under good light. I’m very impressed with that.

Taking a photo wasn’t rocket science, there’s plenty of source for that around. But photos that are large (typically 3,000 x 2,000 pixels) need to be reduced and converted. I set a maximum largest dimension of 750 and ran this loop to calculate the new size.

                
                float height = App.Height;
                float width = App.Width;
                float ratio = height/width;
                while (height > 750 || width > 750)
                {
                    height -= 100;
                    width = height/ratio;
                }

then this code to rescale the bitmap and compress to a jpg with a quality of 80.

                
                var reducedBitmap = Bitmap.CreateScaledBitmap(App.bitmap, (int)width, (int)height,false);
                StatusView.Text = "Processing Image";
                var stream = new MemoryStream();
                reducedBitmap.Compress(Bitmap.CompressFormat.Jpeg, 80, stream);
                var bitmapData = stream.ToArray();

Leaving me with an array of bytes to upload. Sending a string and an image together in one http post is a little bit harder. Here’s how I did it. I found Brian Grinstead’s post and used his FormUpload class.

                var postParameters = new Dictionary<string, object>();
                postParameters.Add("barcode", Barcode.Text);
                postParameters.Add("file", new FormUpload.FileParameter(bitmapData, "image.jpg", "image/jpg"));

                // Create request and receive response
                var postURL = UploadUrl;
                var userAgent = "Someone";
                var webResponse = FormUpload.MultipartFormDataPost(postURL, userAgent, postParameters);

As easy as that!

XML

Unless you want to write it in code, you’ll find that you use XML for lots of things- the Android Manifest file, the layout of each form, strings, styles and preferences. Plus if you are targetting specific versions, screen sizes, you’ll have versions for that as well. It can add up to a lot of XML files. The layout alone is 65 lines long for just three buttons, one image button, one image view plus a couple of labels.

Screen shot of App

Debugging on Android

You have a choice of running the Xamarin simulator in VirtualBox which is ok but doesn’t really work well for scanning barcodes or taking photos so I found the USB device drivers for two handsets I had and debugged on the phones. For some phones the drivers are easy to find, others not so. Once they’re installed, ADB (Android Debug Bridge) is your friend to get going and after that just plug the phone in and Xamarin picks it up.

I found a slight bug and I’m not sure if it’s Xamarin or the phone’s drivers. I’d make a change do a rebuild and deploy and even though it said the previous version was removed it ran the previous version. Uninstalling the App fixed that but it only happened on three occasions so not a big deal.

Distribution

This wasn’t for the Google Play store but a private client. All I had to do was build for Release then click Export Android Package on the build menu. That creates two .apk files and I uploaded the one that had signed in its name.

Did you know that an .apk file like a java .jar file is actually a zip file? Just rename the extension to .zip and you can see everything in it. Note that the XML files may be XML binary files but there are tools to convert them to text XML files.

I’ve been converting a major App and the Migrate tool works very well but I found a couple of cases, which either it didn’t do or I messed up.

The first is when using a nint as an index in a List. It doesn’t work, as the .NET generics know nothing about nints. So just cast it to an int like this below where _itemsList is a List

Text = _itemsList[(int)row];

Converting NSDate to System.DateTime and vice versa

It’s now a compile error to assign a DateTime to a NSDate or vice versa, the implicit conversion no longer works. I think this is to highlight a subtle bug to do with NSDate is always an UTC time and DateTime is default set to DateTimeKind.Unspecified (when read from database) or DateTimeKind.Locale (when set with DateTime.Today) Source is this stackoverflow answer.

Thankfully the Foundation.NSDate class includes two explicit operators that do the job for you.
NSDate in Xamarin Object Browser


So this works.

            NSDate nDate = (NSDate) DateTime.Now;
            DateTime dDate = (DateTime) nDate;

AVAudioRecorder Changes

The settings used to create an AVAudioRecorder have changed from a NSDictionary to AudioSettings, and the AVAudioRecorder.ToUrl method no longer exists.

Instead create avsettings like this and use the factory method Create to create the AVAudioRecorder.

//Set Settings with the Values and Keys to create the NSDictionary
            avsettings = new AudioSettings(NSDictionary.FromObjectsAndKeys(values, keys));

//Set recorder parameters
            recorder = AVAudioRecorder.Create(url, avsettings, out error);

It’s kind of obvious in retrospect about enabling or disabling animation. It’s part of some code where I’ve added a UIImage onto a MkMapView (Apple’s iOS Controls for displaying a map), that centres the map on your current location when you click it. The button is a gun sight type icon and I’ve located in the bottom right edge of the map.

    private UIButton btnCurrentLoc; // defined at the class level

// in ViewDidLoad
    var ImageCurrentLoc = UIImage.FromBundle("images/currentloc.png");
    ImageCurrentLoc.ImageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal);

    btnCurrentLoc = new UIButton() { TintColor = UIColor.Black };
    btnCurrentLoc.SetImage(ImageCurrentLoc, UIControlState.Normal);
    btnCurrentLoc.Frame = new RectangleF(View.Frame.Width-50, View.Frame.Height - 100, ImageCurrentLoc.Size.Width, ImageCurrentLoc.Size.Height);

    btnCurrentLoc.TouchUpInside += (s, e) =>
                {
                    map.SetCenterCoordinate(map.UserLocation.Location.Coordinate,true);  // animated
                    //map.CenterCoordinate = map.UserLocation.Location.Coordinate;       // not animated, moves directly            
                }; 

  View.AddSubview(map);            // has to be this way round! map first then control
  View.AddSubview(btnCurrentLoc);

Note that you add the map first to the view then the button.

As it’s commented if you click the btnCurrentLoc button, it will animate the map so you can see it scroll to your location. If you comment out that line and uncomment the line below, when you click the button it will move there immediately.

iPhone MkMapView with home button

My Visual Studio started taking a longish- three to four minutes to load up and then the same when I opened a project. This started after the last Xamarin update. I’ve always found that for best results I have to reboot after an update and then unpair and pair. Sometimes my Mac Mini shows up in the Connection dialog as an IP address and sometime as the name. As the ip is DHCP provided, I figured out using the name was best however..

To cut a long story short, Zone Alarm did not like DNS queries for the Virgin DNS ( 194.168.4.100 and 194.168.8.100 port 53 ) which were resolving the Mac Mini and it was those being blocked that slowed VS down. I figure it must have been a timeout plus maybe some kind of fallback as I could still build projects on the Mac.

So if you ever get that kind of slow down, check your firewall!

I’ve not mastered AutoLayout, but if you want to resize a label, there’s a few of things you need to do. The image below shows a slider used to control a font size.

 

Resizing a label with a slider

Here I’ve omitted the declarations but fontlabel is a UiLabel, Support.FontSize is a float, and fontSlider is a UISlider. This is the code.

            fontlabel = new UILabel()
            {
                Text = @"Font Size",
                Lines = 1,               
                TextColor = Support.uTextColor,
                BackgroundColor = Support.BackColor,
                LineBreakMode = UILineBreakMode.WordWrap,
                Frame = new RectangleF(10, 320, Frame.Width - 20, 30),
                Font = UIFont.FromName("Helvetica", Support.FontSize)
            };
            fontlabel.SizeToFit();
            AddSubview(fontlabel);
            fontslider = new UISlider(new RectangleF(5, 350, Frame.Width - 5, 30))
            {
                MinValue = 11f,
                MaxValue = 30f,
                Value = Support.FontSize
            };
            fontslider.ValueChanged += HandleFontSliderChanged;

        private void HandleFontSliderChanged(object sender, EventArgs e)
        {
            Support.FontSize = fontslider.Value;
            fontlabel.Font = UIFont.FromName("Helvetica", Support.FontSize);
            fontlabel.SizeToFit();
        }

Allow enough room for the label to fit at its largest size and having changed the font size in the Slider event, call SizeToFit() on the label. Otherwise you’ll find that the text is truncated when the label is made larger.

Near the end of a project and there’s nothing worse than getting a new release of the development software and getting a real big scare. I got 7.0.4 this morning with the new pairing of Visual Studio to the Xamarin.iOS Build Host running on the Mac.

Mac and Windows Pairing Dialogs

That worked well, but the scare came when I went to upload the newly built app on to my device and got a No Provisioning Profiles Found build error. Weird but as I’d renewed my developer account with Apple yesterday, possibly it was related to that but no it showed fine. The profiles on the iPhone showed fine as well in the Xcode organiser.

Dev profiles in Xcode's organiser

I was a bit worried because my Mac is only 10.7.5 and can’t run Xcode 5 or develop for iOS 7. It will be replaced shortly. However it turned out to be a lot simpler than that. In the Xamarin Studio Preferences, the Developer accounts had been zapped. Just adding my Apple account back in sorted it.

Xamarin Studio Developer Accounts

I added Default.png and the other variations for iPhone 5, retina display fils and they appeared in the Resources folder. These are for loading screens and are built in as part of the bundle after you add them.

However in doing some work on rolling my own Splash screen so it can have text added, I decided to remove them.

In Xamarin Studio (on Mac) you can unset them, but compiling gave 2 errors about missing files. Looking in the info.plist on the source tab revealed that two strings were still there so I manually removed those. I did a clean then a Build and still got the errors. I did a clean and a Rebuild and still got the same errors. Finally I closed Xamarin Studio down, then reopened it and it built fine. Just a minor irritation.

Another Xamarin update- I expect there to be quite a few with iOS 7 now hitting alpha on the Xamarin Studio front. However, MonoDoc keeps wanting to have Apple documentation merged with Xamarin.iOS documentation. Even though it’s done it already. It’s deja vu all over again. Last time it was Visual Studio!

monodoc-update

I talked earlier this week about these and have done further work. The problem I was solving was this: The App will be installed from the App Store but is only for a subset of users. It would be very bad if anyone else was able to use it.

So When it’s first run, it looks for Config info. As it’s never been run and this config info is set internally, it comes up with a screen asking for a few fields to be filled in, but only saves the data if a master password is entered. This information is saved out (password fields are used) and then the App kills the main View, recreates it ie effectively it restarts and upon finding the correct information it works properly. This was done in the View’s ViewDidLoad.

What I found was that the ViewWillAppear only appears to fire the first time and not the second following the View being disposed (UiViews are disposable) and then recreated in the a method in ViewController.