Wednesday, October 18, 2017

View Controller Programming Guide for iOS - Defining Your Subclass

View Controller Programming Guide for iOS

Defining Your Subclass

Displaying Your Views at Runtime

  1. Instantiates views using the information in your storyboard file. 
  2. Connects all outlets and actions. 
  3. Assigns the root view to the view controller’s view property. 
  4. Calls the view controller’s awakeFromNib method. (When this method is called, the view controller’s trait collection is empty and views may not be in their final positions. )
  5. Calls the view controller’s viewDidLoad method. (Use this method to add or remove views, modify layout constraints, and load data for your views.)
  6. Calls the view controller’s viewWillAppear: method to let it know that its views are about to appear onscreen. 
  7. Updates the layout of the views. 
  8. Displays the views onscreen. 
  9. Calls the viewDidAppear: method when the views are onscreen.
PS: When you add, remove, or modify the size or position of views, remember to add and remove any constraints that apply to those views. Making layout-related changes to your view hierarchy causes UIKit to mark the layout as dirty. During the next update cycle, the layout engine computes the size and position of views using the current layout constraints and applies those changes to the view hierarchy.

Managing View Layout

  1. Updates the trait collections of the view controller and its views
  2. Calls the view controller’s viewWillLayoutSubviews method.
  3. Calls the containerViewWillLayoutSubviews method of the current UIPresentationController object. 
  4. Calls the layoutSubviews method of view controller’s root view. The default implementation of this method computes the new layout information using the available constraints. The method then traverses the view hierarchy and calls layoutSubviews for each subview. 
  5. Applies the computed layout information to the views. 
  6. Calls the view controller’s viewDidLayoutSubviews method. 
  7. Calls the containerViewDidLayoutSubviews method of the current UIPresentationController object.

Good to know:

  • Use Auto Layout
  • Take advantage of the top and bottom layout guides
  • Remember to update constraints when adding or removing views
  • Remove constraints temporarily while animating your view controller’s views.

Tuesday, March 22, 2016

Android Parcelable Object

Mission: 
Send one custom object or a list of them from one Activity class to another Activity class.

Before we start, let's take a look at what I have in the project.
  • An Activity Class named FirstActivity.
  • An Activity Class named SecondActivity.
  • An Custom Object Class named City.
public class City {
    private String name;
    private double longitude;
    private double latitude;

    // Constructor, Setter and Getter
}

As you could see, the City class is composed of three properties, name, longitude and latitude with appropriate constructor, setters and getters.


How can we send a list of city objects from FirstActivity to SecondActivity? 

The only tunnel for this task is send the data within an Intent. Intent is like an envelope help you send bunch of data in it to the right place, like an Activity. But the problem is the Intent object could mostly only carry primary type objects, like int, double, String, etc, other than custom object. So, when you need to send a custom object, or list of custom objects along with an Intent object, you need to make this custom object class "Parcelable". It's like pack your data into a parcel, then send it through an Intent object. In the new Activity, when Intent object is received, the parcel will be opened (resolved).


Here is how we could do it:

1, In the City.java file, declare class will implement Parcelable Interface.
import android.os.Parcelable;

public class City implements Parcelable{
    private String name;
    private double longitude;
    private double latitude;

    // Constructor, Setter and Getter
}

Now the Custom Class will be Parcelable, could be carried along by the Intent object.

2, Implement Required Methods for Parcelable Interface.
    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeDouble(longitude);
        dest.writeDouble(latitude);
    }
The first method defines the kind of object you are going to Parcel, you can use the hashCode() here.
The second method is actually trying to pack your object data into the parcel.

PS: If some of the property's type is not primary, you need to "Flat" it to primary type object first. E.g. You have a enum type property, then you need to convert it to int value, then use dest.writeInt(value);

Tip: In Android Studio, when there is an error, there will be a red bulb to indicate the Android Studio has a solution for this. After you add "implements Parcelable" after "class City", the red bulb will show up, click on it and choose implement the Parcelable Interface, it will automatically implements these two methods for you.

3, Classes implementing the Parcelable interface must also have a non-null static field called CREATOR of a type that implements the Parcelable.Creator interface. This is the step where you could unpack data from a parcel, then construct it into a new object.
    protected City(Parcel in) {
        name = in.readString();
        longitude = in.readDouble();
        latitude = in.readDouble();
    }

    public static final Creator CREATOR = new Creator() {
        @Override
        public City createFromParcel(Parcel in) {
            return new City(in);
        }

        @Override
        public City[] newArray(int size) {
            return new City[size];
        }
    };

Tip: In Android Studio, these two methods could also be auto implemented in the way introduced in the last step.

4, How to use it?
  • Send an Intent object with Parcelable object.
  •     City city = new City("New York", 40.7056497, -73.9780035);
        Intent intent = new Intent(getApplicationContext(), SecondActivity.class);
        intent.putExtra("Intent String ID for City Object", city);
        startActivity(intent);
    
  • Send an Intent object with list of Parcelable objects.
  •     City cityNY = new City("New York", 40.7056497, -73.9780035);
        City cityLA = new City("Los Angeles", 34.0204989, -118.4117325);
        ArrayList cityList = new ArrayList<>();
        cityList.add(cityNY);
        cityList.add(cityLA);
        Intent intent = new Intent(getApplicationContext(), SecondActivity.class);
        intent.putParcelableArrayListExtra("Intent String ID for City Object List", cityList);
        startActivity(intent);
    
  • Resolve a Parcelable object from an Intent object.
  •     Intent intent = getIntent();
        City city = intent.getExtras().getParcelable("Intent String ID for City Object");
  • Resolve a list of Parcelable objects from an Intent object.
  •     Intent intent = getIntent();
        ArrayList cityList = intent.getExtras().getParcelableArrayList("Intent String ID for City Object List");
    
  • Array of Parcelable object could also be sent and received along with Intent object.

Full source code of City:

package com.antonio081014.demo.android.parcelabledemo;

import android.os.Parcel;
import android.os.Parcelable;

public class City implements Parcelable {
    private String name;
    private double longitude;
    private double latitude;

    // Constructor, Setter and Getter
    public City(String name, double longitude, double latitude) {
        this.name = name;
        this.longitude = longitude;
        this.latitude = latitude;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getLongitude() {
        return longitude;
    }

    public void setLongitude(double longitude) {
        this.longitude = longitude;
    }

    public double getLatitude() {
        return latitude;
    }

    public void setLatitude(double latitude) {
        this.latitude = latitude;
    }
    
    protected City(Parcel in) {
        name = in.readString();
        longitude = in.readDouble();
        latitude = in.readDouble();
    }

    public static final Creator CREATOR = new Creator() {
        @Override
        public City createFromParcel(Parcel in) {
            return new City(in);
        }

        @Override
        public City[] newArray(int size) {
            return new City[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeDouble(longitude);
        dest.writeDouble(latitude);
    }
}

Friday, February 12, 2016

Stored Property Observer in Swift

Firstly, let's take a look at the following Code Example:

In Swift,

Stored Property has two function to notify observer when it will be set(trying but before it is set, willSet) and it has been set(trying and already set it, didSet). So, every time trying to set the property, the willSet function will be called first, followed by didSet.

Here I did an interesting experiment: trying to modify the property while these two observing functions are called.

In the willSet function, assign it to a new String “Hi” does not do anything. It looks like a temporary assignment, and then assign it back very soon, because in the next calling function didSet, str is “Hello, Antonio”, rather than “Hi”, also the oldValue was not changed either.

While, in the didSet function, assign it to a new String “Hi” does actually assign the String “Hi” to this property, it changed the property’s value indeed, though the oldValue is still “Hello, playground”.

This is because the willSet function is called before the Stored Property has actually been set, while the “didSet” function is called after the Stored Property has already been set. So, in willSet function, assign a new String will not change the property value, since it will be assigned soon, while in didSet function, the property has been assigned, there is no other assignment here, so if it’s assigned by a new String value, this property value will be changed.

There is one thing deserve to note, assign new value to the property in the willSet and didSet function will not invoke recursively willSet nor didSet observing function.