Wednesday, March 21, 2012

iOS: Building an app for Ad-Hoc Distribution

1. Create a Distribution Ad-Hoc Provisioning Profile in the iOS Provisioning Portal. Download the profile and install it onto the computer that builds the app.
2. In the project Build Settings, change the Code Signing Identity to the Ad-Hoc profile.
3. In the Product menu, click Build For -> Archiving.
4. In the Product menu, click Archive.
6. In the Window menu, open the Organizer.
7. In the Organizer, click the Archives tab. You should be able to find the new archive.
8. Click Distribute...
9. Choose "Save for Enterprise or Ad-Hoc Deployment" and click Next.
10. Choose the same Ad-Hoc profile.
11. Choose a name and folder for your app binary. It will be saved in the .ipa format.

To test your app binary, you can give the profile (.mobileprovision) and the app binary (.ipa) to your beta tester. Make sure that when you create the profile, their device IDs are also included in the profile. Then, they will need to drag the profile and the app binary into iTunes->Apps page. When the testers syncs the iOS device, the app should appear on the device.

Saturday, March 10, 2012

The Maximum Number of Rows in UITableViewController

I am quite interested in find the number of rows that UITableViewController supports. The result is between 40,000,000 and 50,000,000:


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // Return the number of rows in the section.
    return 40000000;
}

A table view with 40,000,000 rows works fine but with 50,000,000 rows it does not.

Saturday, February 25, 2012

iOS: Run-time error when using a class category in a different static library

When you are calling a member function defined in a class category (https://developer.apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/chapters/occategories.html) from a different static library, you might see a run-time exception of "selector not recognized".

This is a well-know issue with Xcode and iOS. There is a technical note from Apple to describe the problem and the workaround by using the -all_load flag or the -force_load flag followed by the archive path:

https://developer.apple.com/library/mac/#qa/qa1490/_index.html

There is also another workaround without defining the flag. In the file that define the class category, add a C function to the .h and .m, and call this function anywhere in the project that you call the member function of the class category. For example, if you have a class category called UIImage (A) in UIImage+A.h and UIImage+A.m, you can add a function like this:


UIImage+A.h
------------------
// This function needs to be called in the file that uses UIImage (A)
void forceLoadUIImageCategoryA(void);

@interface UIImage (A)
...
@end


UIImage+A.m
------------------
void forceLoadUIImageCategoryA(void)
{
    // Do nothing
}

@implementation UIImage (A)
...
@end


In the file that uses any member functions of UIImage (A), call forceLoadUIImageCategoryA(); anywhere. Calling this C function will make Xcode link the .h and .m.

Xcode warning: no previous prototype for function ...

This warning appears when you define a C-style function like this:

In .h:
void foo();

In .m:
void foo()
{
}

However, according to C standard, you should define the function with the void keyword if there is no parameters in your function:

In .h:
void foo(void);

In .m:
void foo(void)
{
}

Sunday, February 12, 2012

Why UIView's autoresizingMask does not work

I was trying to use to autoresizingMask in a UILabel to resize the width when the device is rotated. Following my first instinct, I wrote label.autoresizingMask = UIViewAutoresizingFlexibleWidth. However, the label didn't resize. Why?

There are a few values for the mask such as UIViewAutoresizingNone, UIViewAutoresizingFlexibleRightMargin, UIViewAutoresizingFlexibleLeftMargin, and UIViewAutoresizingFlexibleHeight. I tried to use UIViewAutoresizingFlexibleRightMargin or UIViewAutoresizingFlexibleLeftMargin but there was no difference.

Wait a minute. If we need to set those values to enable flexible margins, width, and height, what does it mean if we leave them unset? If I only set the autoresizingMask property to UIViewAutoresizingFlexibleWidth, it actually means Flexible Width AND Fixed Left Margin AND Fixed Right Margin. Even though the width is flexible, the fixed left and right margins do not allow the flexible width to expand or shrink.

Now, it is clear that when we need to set flexible width or height, we must also set  at least a corresponding margin to be flexible at the same time. For example, if we want the label to expand the end of the text (assuming that the text goes from left to right) when the width changes, we should set autoresizingMask to UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin so that the label can expand it's width at the right margin.

Sunday, February 5, 2012

Use of undeclared identifier 'kUTTypeImage'

This constant is declared in UTCoreTypes.h in the MobileCoreServices framework. In the .m files that you use the constant kUTTypeImage, import the above .h file:

   #import <MobileCoreServices/UTCoreTypes.h>

In the application project, you also need to include the MobileCoreServices framework.

Thursday, January 12, 2012

Rendering images in a background thread in Qt

QImage instead of QPainter should be used. According to the QImage class reference, QPainter can be used to draw directly onto images. When using QPainter on a QImage, the painting can be performed in another thread than the current GUI thread.

Render-to-texture in OpenSceneGraph

 // Create a render-to-texture camera with an absolute
 // reference frame and pre-render
 osg::ref_ptr<osg::Camera> camera = new osg::Camera();
 camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
 camera->setRenderOrder(osg::Camera::PRE_RENDER);

 // Set the camera's projection matrix to reflect the
 // world of the geometries that will appear in the texture
 camera->setProjectionMatrixAsOrtho2D(minx, maxx, miny, maxy);

 //Set the camera's viewport to the same size of the texture
 camera->setViewport(0, 0, textureWidth, textureHeight);

 // Set the camera's clear color and masks
 camera->setClearColor(clearColor);
 camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

 //Create the texture image
 osg::ref_ptr<osg::Image> image = new osg::Image();
 image->allocateImage(textureWidth,
                      textureHeight,
                      1,   // 2D texture is 1 pixel deep
                      GL_RGBA,
                      GL_UNSIGNED_BYTE);
 image->setInternalTextureFormat(GL_RGBA8);

 // Create the texture object and set the image
 osg::ref_ptr<osg::Texture2D> texture2D = new osg::Texture2D();
 texture2D->setImage(image);

 // Attach the texture to the camera. You can also attach
 // the image to the camera instead. Attaching the image may
 // work better in the Windows Remote Desktop
 camera->attach(osg::Camera::COLOR_BUFFER, texture2D.get());

 // Add the geometries that will appear in the texture to the camera
 camera->addChild(geometriesInTexture);

 // Create a geode to display the texture
 osg::ref_ptr<osg::Geode> geode = new osg::Geode();

 // Create a geometry for the geode
 osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry();
 geode->addDrawable(geometry);

 // The coordinates can be different if we want to display the
 // texture at a location different from the coordinates of the
 // geometries inside the texture
 osg::ref_ptr<osg::Vec3Array> vertexArray = new osg::Vec3Array();
 vertexArray->push_back(osg::Vec3(minx, miny, minz));
 vertexArray->push_back(osg::Vec3(maxx, miny, minz));
 vertexArray->push_back(osg::Vec3(maxx, maxy, minz));
 vertexArray->push_back(osg::Vec3(minx, maxy, minz));
 geometry->setVertexArray(vertexArray);

 // The geometry color for the texture mapping should be white
 // (1.f, 1.f, 1.f, 1.f) so that color blending won't affect
 // the texture real color
 osg::ref_ptr<osg::Vec4Array> colorArray = new osg::Vec4Array();
 colorArray->push_back(osg::Vec4(1.f, 1.f, 1.f, 1.f));
 geometry->setColorArray(colorArray);
 geometry->setColorBinding(osg::Geometry::BIND_OVERALL);

 // We are using the entire texture by using the four corners
 // of the texture coordinates
 osg::ref_ptr<osg::Vec2Array> texCoordArray = new osg::Vec2Array();
 texCoordArray->push_back(osg::Vec2(0.f, 0.f));
 texCoordArray->push_back(osg::Vec2(1.f, 0.f));
 texCoordArray->push_back(osg::Vec2(1.f, 1.f));
 texCoordArray->push_back(osg::Vec2(0.f, 1.f));
 geometry->setTexCoordArray(0, texCoordArray);

 // We always create a square texture.
 geometry->addPrimitiveSet(
    new osg::DrawArrays(GL_TRIANGLE_FAN, 0, 4));

 // Add the texture to the geode
 geode->getOrCreateStateSet()->setTextureAttributeAndModes(
    0, texture2D, osg::StateAttribute::ON);

 // Make sure that we are using color blending
 // and the transparent bin, since the features may not
 // cover the entire texture and the empty
 // space should be transparent.
 geode->getOrCreateStateSet()->setRenderingHint(
    osg::StateSet::TRANSPARENT_BIN);
 osg::ref_ptr<osg::BlendFunc> blendFunc = new osg::BlendFunc;
 blendFunc->setFunction(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 geode->getOrCreateStateSet()->setAttributeAndModes(blendFunc);

 // Add both the rtt camera and the geode to the scene graph
 sceneGraph->addChild(camera);
 sceneGraph->addChild(geode);

* NOTE: If we only want to render one frame on the texture, we can remove the camera from the scene graph after the frame is rendered. The geode will still display the static texture in the scene graph properly.

Tuesday, January 3, 2012

OpenGL Version in Windows Remote Desktop

Qt shows that the current version and the only supported version in Windows Remote Desktop is 1.1.

Changing the current OpenGL version in Qt

We can change the current OpenGL version by creating a QGLFormat object and calling QGLFormat::setVersion, then passing the format into the QGLWidget's contructor.

NOTE: OpenGL 3.3 is being shown on my current machine with an ATI Radeon HD 2400 Pro graphics card. However, setting the version to 3.2 will give out a lot of warnings and nothing is being drawn on the screen. Setting the version to any versions between 1.x and 2.x will end up with version 3.3. The only version that I can successfully set to is 3.0 only.

The supported OpenGL versions in Qt

The supported OpenGL versions can be queried from QGLFormat in a QGLWidget. The function QGLFormat::openGLVersionFlags returns all the supported versions of the platform.

The current OpenGL version in Qt

The OpenGL version can queried from QGLFormat in a QGLWidget . The function QGLFormat::majorVersion and QGLFormat::minorVersion returns the current OpenGL version the platform is using.

Monday, December 12, 2011

QT_FORWARD_DECLARE_CLASS should be used for forward declaration of Qt classes

Using QT_FORWARD_DECLARE_CLASS, the forward declaration will still work if someone configures Qt with -qtnamespace:

For example, if someone configures a MyNamespace, QT_FORWARD_DECLARE_CLASS(QWidget) becomes:

namespace MyNamespace { class QWidget; }
using ::MyNamespace::QWidget;

whereas normal "class QWidget" forward declaration will cause a conflict since the class is declared outside the namespace.

Saturday, August 20, 2011

Building OpenSceneGraph for iOS [In Progress]

After spending a few hours on trying to build OpenSceneGraph 3.0.1 on Xcode 4.2 for iOS 4.3/5.0, I finally gave up ...

The README.txt in the official OSG package describes how to generate an Xcode project using CMake. I tried to use CMake 2.8.5. Following the cmake command line described in README.txt, I got an error about not able to build a test project. Then I tried CMake GUI, but it still didn't work.

I searched the web and found a modified version at git://github.com/stmh/osg.git. This time, CMake GUI generated an Xcode project. However, when I tried to open and build this project in XCode 4.2, I got the following error:

OpenSceneGraph-2.9.10/src/OpenThreads/pthreads/PThread.c++:1: error: -mmacosx-version-min not allowed with -miphoneos-version-min

Reference: http://forum.openscenegraph.org/viewtopic.php?t=7433

It seems that CMake always made a mistake in setting the Architectures, Base SDK, Supported Platforms, and Valid Architectures consistently. Then I created a new OpenGL ES game project in XCode and followed the settings:

Architectures: Standard (armv7)
Base SDK: Latest iOS (iOS 5.0)
Supported Platforms: iphonesimulator iphoneos
Valid Architectures: armv6 armv7 armv7f armv7k

I also made sure that i386 did not appear in the CMAKE_CXX_FLAGS since I was building OSG for iOS Device.

The error disappeared, but a lot of new errors showed up:

GL

'*' was not declared in this scope
'glLoadMatrixf' was not declared in this scope
...

It seems that XCode was not able to find the OpenGL ES library. The XCode project does not contain the OpenGLES.framework. Therefore, I created a new OpenGL ES project and added the OSG project to it. I also set the Header Search Paths to point to the root of the OpenSceneGraph package.

However, I still got the following error at the end:

'vector' file not found in lwo2chunks.h

It seems XCode cannot locate the standard library. I have no clue how that can happen.

============

I just found a post about how to minimize the size of the osg library for iphone:
http://forum.openscenegraph.org/viewtopic.php?t=7224

Summary:
we should build the library in arm 7 only for the device, since arm 7 is for 3GS and above. I think there is no point to build the library in arm 6 now, since iPhone 3G or earlier are relatively old now.

We also only need OpenThreads, osg, osgDB, osgGA, osgUtil and osgViewer. We can probably throw away all of the plugins.

We should also build the osg libs with Optimization set to Fastest-Smallest.

BTW, the same post also mentions that display lists are not available in OpenGL ES. Therefore, we should turn off OSG_GL_DISPLAYLISTS_AVAILABLE.

==========

I found another post that mentions Apple loosened the terms on their license agreement to allow for dynamic linking and plug-ins now:

http://forum.openscenegraph.org/viewtopic.php?t=6582

BTW, Thomas Hogarth is a person who successfully built OSG for iOS and also published an iOS game called Alient Attack.


Wednesday, July 27, 2011

Qt::ApplicationShortcut

If you set the shortcut context to Qt::ApplicationShortcut (or maybe other types too) in a QAction, you have to add the action to a widget by calling QWidget::addAction() before it can be used. This doesn't mean that the action will be visible. Adding the action to the widget will just make the shortcut part of the widget's event loop.

Monday, July 18, 2011

Dictionary: aspire

verb
1. 立志
2. 熱望


example
GitX doesn't aspire to be a complete interface for the wide variety of git command-line programs with their ever-growing number of options.

Monday, June 6, 2011

osg::ClearNode clears at the start of rendering

From what I read from the mail archive below and what I tried, osg::ClearNode actually clears at the start of rendering and not just when it's reached in the draw traversal.

Reference:
http://www.mail-archive.com/osg-users@lists.openscenegraph.org/msg27321.html

Saturday, March 5, 2011

Don't retain UIColor, and don't release it

UIColor manages the lifetime of colors it returns. The caller is not supposed to retain it and release it.

Objective-C's protocol's property cannot be inherited

None of the Apple's Objective-C documents or programming guides that I have seen show an example of declaring a property in a protocol and the protocol is inherited. I declared a property in a protocol by Xcode fails to recognize the property.

Friday, February 25, 2011

Using an osg::ref_ptr for 'this' in the constructor is a bad idea

Inside a constructor, the reference count of 'this' is still zero. If you use a local osg::ref_ptr to reference 'this', the reference count will increment to one. However, when the constructor ends, the lifetime of the osg::ref_ptr also ends, and the reference count will become zero again, and 'this' will be destroyed.