Saturday, 25 May 2013

iOS: macro for minimum supported target iOS version (deployment target)

Each Xcode project has a deployment target, that is the minimum iOS version your application should support.

Sometimes you need to use different code for different deployment target, here is an example:

#if __IPHONE_OS_VERSION_MIN_REQUIRED < 60000
[self presentModalViewController:webViewController animated:YES];
#else
[self presentViewController:webViewController animated:YES completed:nil];

#endif

Thursday, 23 May 2013

UIKit: Simple way to disable gesture recognizer in subview

Sometimes you have gesture recognizer with a view but want to disable it for a subview (e.g. you use the subview as a toolbar). By default you can click "through" the subview as if it does not exist, which can cause confusion. A simple way to disable gesture recognizer in a subview is in UI builder to drag new a gesture recognizer onto the subview, which associates a gesture recognizer with the subview which does nothing when the gesture is recognized.

If you want the child of the subview (e.g. some buttons in this subview) to receive taps, uncheck its "Canceled in view" property.

OpenGL ES 1.1: How to convert world coordinates to UIView coordinates

    Assuming world coordinates is in worldp of type vec4, UIView window size is in bounds.size, the following code converts world coordinates to UIView coordinates:

    mat4 modelview;
    mat4 projection;
    glGetFloatv(GL_MODELVIEW_MATRIX, (float*)modelview);
    glGetFloatv(GL_PROJECTION_MATRIX, (float*)projection);
    vec4 eyep = worldp * modelview * projection;
    eyep = eyep/eyep.w;
    screenp.x = (1+eyep.x)/2*bounds.size.width;
    screenp.y = (1-eyep.y)/2*bounds.size.height;

Friday, 17 May 2013

Eclipse: recover overwritten files

If you overwrote a new file with an old file by mistake, Eclipse may be your only life saver. It has a feature called local history, which automatically records all the changes you made to a file. If you want to revert a file to an old edition, just right click the file in the left panel, and choose team/local history, then select a date to recover.

That's one reason I like Eclipse. Actually today it just saved me several days' of work when I accidentally reverted my file to an old revision in a version control system by mistake.

Sunday, 12 May 2013

OpenGL ES 1.1: texture mapped object has wrong color

If you use texture to get exact color for an object, make sure at the texture coordinate the texture does not change color suddenly. If so, you will get interpolated color instead of exact color. If the texture coordinates are at the corner or boundary of the texture, make sure the color at the other 3 corners or the reflected boundary are the same, otherwise you may get unwanted interpolated color.

OpenGL ES 1.1: lighted texture mapped object is too dark

Usually the texture is used as the diffuse material for the object. It is timed with the diffuse light to get the diffuse part of the lighted object color.

If the object looks too dark, you can increase the luminance of the diffuse light. If the luminance of the diffuse light is already 1, you may want to increase the luminance of your texture.


OpenGL ES 1.1 how to choose proper value for ambient/diffuse/specular color for light and material

Usually the color of a lighted object is summation of three parts: ambient, diffuse and specular.

The ambient part is the ambient light color times the object ambient material color. It does not depend on light position nor eye position.

The diffuse part is the diffuse light color times the object diffuse material color times a factor. It depends on light position but not eye position.

The specular part is the specular light color times the object specular material color times a factor. It depends on both light position and eye position.

There could be an emission part, which is similar to ambient part but does not need a light.

To view the contribution of the three parts to the final object color, you can enable just one type of light or material and disable other types of light or material and see the rendered object.

Since the three parts are summed together, you cannot let them all have strength 1, otherwise the color of the object becomes too bright like a photo with over exposure. You need to give each part a weight so that their summation does not exceed 1 in most positions.

The diffuse part provides most of the visual cue to the shape of the object. Therefore I usually give it a weight of 0.7, that is, diffuse light illuminance times diffuse material illuminance is around 0.7.

 You may want some ambient color if you do not want to have dark part of the object. However, too much ambient part gives your object a washed out feeling, so you do not want too much of it. A weight of 0.3 seems to be a good choice.

The specular part gives a shiny spot on your object. Usually a weight of 0.1 is good choice.  There is another parameter called shininess to control how much this shiny spot spreads out. A value of about 100 gives you a small bright spot whereas a value of 10 gives you less bright shiny patches.



Thursday, 9 May 2013

iOS: cannot find In-App-Purchase product in sandbox app store

You should be able to see In-App-Purchase products in sandbox app store even before you submit your application for review. Changes made in iTunes connect about In-App-Purchase should be immediately seen in sandbox app store.

One cause of problem is your IAP product ID is not unique. IAP product ID needs to be unique in the app store, not just unique in your app. A product ID like "Upgrade" won't work. You need to make it really unique by using reverse domain name like "com.mycompany.mypapp.myprod".

Another requirement is that your app bundle ID needs to match what you use in iTunes Connect. You need to first create a new bundle ID supporting IAP in iOS developer's portal then use it to create a new app in iTunes Connect.

If you change your bundle ID in Xcode, you need to delete the old app in iPhone simulator manually, otherwise Xcode may get confused and run the old app, which will confuse you since all your changes seem to have no effect.

Monday, 6 May 2013

iOS: Using lazy loading to improve application loading time

If your application needs to load some large resource and do some lengthy processing before it can display something, you need to do those in a thread other than the main UI thread. Otherwise your user will see your default launch image for a long time and may become impatient.

This is called lazy loading. However it introduces complexity into your application since now some resources may be not ready when you display your content.

If you display content by polling changes (e.g. OpenGL ES doFrame), your resource processing threads need to let the display thread know content has changed and the display thread needs to check if a resource is ready and then decide how to display.

Otherwise, the resource processing threads need to update the view by sending message or notification to the view controller.

Sunday, 5 May 2013

iOS: OpenGL ES frame is not updated for changes made in another thread

It is common to use display link in iOS to set a frame rate for OpenGL ES and use a function doFrame to update frame regularly.

-(void) doFrame {

  if (_needUpdate) {      // line A
    if ( // condition is satisfied) {
      // draw the scene
      _needUpdate = false;  // line B
    }
  }

}

In another thread, you do the following

//change the scene
_needUpdate = true;  // line C

so that the scene will be redrawn in doFrame.

However there is a bug with this approach. If execution time between line A and line B is long, there is high chance that line C happens between line A and line B and has no effect. The symptom is that frame is not updated for changes made in another thread.

One simple workaround is to move line B up and make the execution time between line A and line B short. Use a counter instead of boolean for _needUpdate could be another solution.





Saturday, 4 May 2013

Workaround about sorting English ordinal numbers or Roman numbers

You may want to sort texts containing English ordinal numbers or Roman numbers by their numerical order instead of dictionary order. For example

1st word
2nd word
3rd word
4th word

Richard I
Richard II
Richard III
Richard IV

One simple workaround for this is to encode them as simple numbers in the text, e.g.

#T001 word
#T002 word
#T003 word
#T004 word

Richard #R01
Richard #R02
Richard #R03
Richard #R04

then they can be sorted by dictionary order. When you need to display them, convert them to ordinal number or Roman number.

Binary file read by std::ifstream is corrupted

By default, std::ifstream is constructed with text mode. On windows, it will convert new line character which may corrupt the binary file.

The fix is to use std::ifstream::binary as the second parameter when construct ifstream for binary file.