Tuesday, July 10, 2012

Loading Images using GCD

The main reason when a app become sluggish is when the main thread is blocked. To make the app to run smoother main thread should not be blocked. Since UIImage calls will not work on secondary thread if there are too many images to load then definitely the app become unresponsive while loading the images.

In the below method I am creating CGImageRef and then creating UIImage using that CGImageRef. Since CG calls will work in secondary thread you can put image loading part to GCD and leave it for GCD to take of image loading part without blocking main thread;)

+(CGImageRef) createCGImageFromFile  : (NSString*) inName
    CGDataProviderRef dataProvider = CGDataProviderCreateWithFilename([[WSFUtilities getResourcePath:inName] UTF8String]);
    // use the data provider to get a CGImage; release the data provider
    CGImageRef image = CGImageCreateWithPNGDataProvider(dataProvider, NULL, NO,
    // make a bitmap context of a suitable size to draw to, forcing decode
    size_t width = CGImageGetWidth(image);
    size_t height = CGImageGetHeight(image);
    unsigned char *imageBuffer = (unsigned char *)malloc(width*height*4);
    CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef imageContext =
    CGBitmapContextCreate(imageBuffer, width, height, 8, width*4, colourSpace,
                          kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little);
    // draw the image to the context, release it
    CGContextDrawImage(imageContext, CGRectMake(0, 0, width, height), image);
    // now get an image ref from the context
    CGImageRef outputImage = CGBitmapContextCreateImage(imageContext);
    return outputImage;//Need to release by the receiver

-(void) setBackgroundImage
// other code...

  dispatch_queue_t loadingQueue = dispatch_queue_create("com.testing.imageloading",NULL);
  dispatch_async(loadingQueue, ^{
                CGImageRef outputImage = [self createCGImageFromFile:imagePath];
               dispatch_async(dispatch_get_main_queue(), ^{
               UIImage *image = [[UIImage alloc] initWithCGImage:outputImage];
               yourImageView.image = image;
              [image release];});

//other code...

Friday, July 6, 2012

UIScrollview scrolling in a non linear fashion!

If you want to implement scrolling something like in the link http://joelb.me/scrollpath/ its not possible using UIScrollView because the contentOffset of the scrollview will increase linearly. Don't worry you can modify the standard scrolling behavior by applying logic something like below code.
In the post I have implemented the UIScrollView in such a way that it scrolls like bazier path and there is a commented code for sine wave testing also. If you comment the bazier code and uncomment the sine wave code… Then the scollview scrolls like sine wave. Cool and simple isn't it :):):)

#Define POINT_A CGPointMake(0, 0)
#Define POINT_B CGPointMake(500, 1024*2)
#Define POINT_C CGPointMake(1000, 1024*1.5)
#Define POINT_D CGPointMake(1800, 0))

#define SCROLLVIEW_HEIGHT (768*3.0)

// simple linear interpolation between two points
CGPoint lerp(CGPoint dest, const CGPoint a, const CGPoint b, const float t)
    CGPoint outPoint;
    outPoint.x = a.x + (b.x-a.x)*t;
    outPoint.y = a.y + (b.y-a.y)*t;
    return outPoint;

// evaluate a point on a bezier-curve. t goes from 0 to 1.0
CGPoint bezier(CGPoint dest, const CGPoint a, const CGPoint b, const CGPoint c, const CGPoint d, const float t)
    CGPoint ab,bc,cd,abbc,bccd;
    ab = lerp(ab, a,b,t);           // point between a and b (green)
    bc = lerp(bc, b,c,t);           // point between b and c (green)
    cd = lerp(cd, c,d,t);           // point between c and d (green)
    abbc = lerp(abbc, ab,bc,t);       // point between ab and bc (blue)
    bccd = lerp(bccd, bc,cd,t);       // point between bc and cd (blue)
    dest = lerp(dest, abbc,bccd,t);   // point on the bezier-curve (black)

    return dest;

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
    float x = [scrollView contentOffset].x;  

    //Sin wave Testing
    //float y = 90 - sin((x / (768*3))*10)*80;

    //Bazier path
    // 4 points define the bezier-curve. These are the points used
    // for the example-images on this page.
    // Will work for n Points also
    CGPoint a = POINT_A;
    CGPoint b = POINT_B;
    CGPoint c = POINT_C;
    CGPoint d = POINT_D;

    CGPoint p = bezier(p, POINT_A, POINT_B, POINT_C, POINT_D,([scrollView contentOffset].x/SCROLLVIEW_HEIGHT));
    x = [scrollView contentOffset].x;
    y = p.y;

    scrollView.contentOffset = CGPointMake(x, y);

Restricting diagonal scrolling in UIScrollview

#Restricting diagonal scrolling

    eScrollLeft = 0,
    eScrollRight = 1,
    eScrollTop = 2,
    eScrollBottom = 3,
    eScrollNone = 4

-(EGRIDVIEW_SCROLLDIRECTION) getScrollDirection : (CGPoint) startPoint endPoint:(CGPoint) endPoint
    EGRIDVIEW_SCROLLDIRECTION direction = eScrollNone;
    int xDirectionOffset = startPoint.x - endPoint.x;
    if(xDirectionOffset > 0)
        xDirection = eScrollLeft;
        xDirection = eScrollRight;
    int yDirectionOffset = startPoint.y - endPoint.y;
    if(yDirectionOffset > 0)
        yDirection = eScrollTop;
        yDirection = eScrollBottom;
    if(abs(xDirectionOffset) > abs(yDirectionOffset))
        direction = xDirection;
        direction = yDirection;

    return direction;

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
    mPreviousTouchPoint = scrollView.contentOffset;

- (void)scrollViewDidScroll:(UIScrollView *)sender
    CGPoint offset = self.scrollView.contentOffset;
    //Cool... Restricting diagonal scrolling
    mSwipeDirection = [self getScrollDirection:mPreviousTouchPoint endPoint:self.scrollView.contentOffset];
    switch (mSwipeDirection) {
        case eScrollLeft:
            self.scrollView.contentOffset = CGPointMake(offset.x, mPreviousTouchPoint.y);
        case eScrollRight:
            self.scrollView.contentOffset = CGPointMake(offset.x, mPreviousTouchPoint.y);
        case eScrollTop:
            self.scrollView.contentOffset = CGPointMake(mPreviousTouchPoint.x, offset.y);
        case eScrollBottom:
            self.scrollView.contentOffset = CGPointMake(mPreviousTouchPoint.x, offset.y);

Adding fade Animation to the scrollview

Here is the simple usage of mapping the values to get fade effect while scrolling, I am not going to write whole code. Here is the logic...

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
    mPreviousTouchPoint = scrollView.contentOffset;

- (void)scrollViewDidScroll:(UIScrollView *)sender
if(animation == fadeIn)
        pageWidth = self.pageSize.size.width;
        page = floor((self.scrollView.contentOffset.x - pageWidth) / pageWidth) + 1;
                 //Mapping values from 0-1024(subview frame) to 0-1(alpha range)
        float NewMax = 1;
        float NewMin = 0;
        float OldMax = pageWidth*(page+1);
        float OldMin = pageWidth*page;
        int OldValue = mScrollView.contentOffset.x;//doubth
        float newValue = (((OldValue - OldMin) * (NewMax - NewMin)) / (OldMax - OldMin)) + NewMin;
               //If it is scrolling to right applying animation to next page
        page = page+1;
                 //If it is scrolling to left applying animation to previous page
        if(mPreviousTouchPoint.x > mScrollView.contentOffset.x)
            page = page - 1;
        if(page < 0 || page >= mNoOfPages)
                //Applying alpha to the subview, where mSubviewsArray contains all the subviews of a       //scrollview
         UIView *nextPage = [mSubviewsArray objectAtIndex:page];
          if(mPreviousTouchPoint.x > mScrollView.contentOffset.x)
                nextPage.alpha = 1-newValue;
                nextPage.alpha = newValue;

Tuesday, June 26, 2012

Some useful matrix methods...

Every time while developing iOS Applications which use matrix related calculations, I used to think how to convert index to matrix co-ordinate etc. So i thought of writing this one -  why to reinvent the wheel every-time :)

-(NSInteger) getIndexFromMatrix : (CGPoint) inPoint size:(CGSize)gridSize
    if( (inPoint.x >= 0 && inPoint.x < gridSize.width) && (inPoint.y >= 0 && inPoint.y < gridSize.height))
        return inPoint.x*(gridSize.width-1) + (inPoint.x+inPoint.y);
    return -1;

-(CGPoint) getMatrixFromIndex : (NSInteger) index size:(CGSize)gridSize
    return CGPointMake(index/(int)gridSize.width, index%(int)gridSize.height);

-(BOOL) isIndexIsDialognal:(CGPoint)index1 andIndex:(CGPoint)index2
    return (abs(index1.x - index2.x)==abs(index1.y - index2.y));

-(NSMutableArray*) adjacentIndicesForIndex:(CGPoint)inPoint matrixSize:(CGSize) matrixSize
    NSMutableArray *array = [NSMutableArray array];
    for (int dx = -1; dx <= 1; ++dx) 
        for (int dy = -1; dy <= 1; ++dy) 
            if (dx != 0 || dy != 0
                CGPoint adjacentIndex = CGPointMake(inPoint.x + dx, inPoint.y + dy);
                if(![self isIndexIsDialognal:adjacentIndex andIndex:inPoint]) //Checking for diagonal elements
                    NSInteger index = [self getIndexFromMatrix:adjacentIndex size:matrixSize];
                    if(index >= 0)
                        [array addObject:[NSNumber numberWithInt:index]];
    return array;

#Some Coolest thinks i have learnt while developing an application

1. Mapping between two range
   //NewValue = (((OldValue - OldMin) * (NewMax - NewMin)) / (OldMax - OldMin)) + NewMin
  //Mapping values between mStartAngle and mEndAngle
  mCurrentValue = (((timeNow - previousTime) * (mEndingAngle - mStartingAngle)) / (previousTime+fireInterval - previousTime)) + mStartingAngle;

 2. To check if a point is inside a convex polygon
// wn_PnPoly(): winding number test for a point in a polygon
//      Input:   P = a point,
//               V[] = vertex points of a polygon V[n+1] with V[n]=V[0]
//      Return:  wn = the winding number (=0 only if P is outside V[])

// isLeft(): tests if a point is Left|On|Right of an infinite line.
//    Input:  three points P0, P1, and P2
//    Return: >0 for P2 left of the line through P0 and P1
//            =0 for P2 on the line
//            <0 for P2 right of the line
//    See: the January 2001 Algorithm "Area of 2D and 3D Triangles and Polygons"
inline int isLeft( point P0, point P1, point P2 )
    return ( (P1.mX - P0.mX) * (P2.mY - P0.mY)
            - (P2.mX - P0.mX) * (P1.mY - P0.mY) );

FFBool isPointInsidePolyGon( point P, point* V, int n )
    int    wn = 0;    // the winding number counter

  // loop through all edges of the polygon
    for (int i=0; i<n; i++) {   // edge from V[i] to V[i+1]
        if (V[i].mY <= P.mY) {         // start y <= P.y
            if (V[i+1].mY > P.mY)      // an upward crossing
                if (isLeft( V[i], V[i+1], P) > 0)  // P left of edge
                    ++wn;            // have a valid up intersect
        else {                       // start y > P.y (no test needed)
            if (V[i+1].mY <= P.mY)     // a downward crossing
                if (isLeft( V[i], V[i+1], P) < 0)  // P right of edge
                    --wn;            // have a valid down intersect
    return wn;

Thursday, June 16, 2011

Some useful scrips/commands

Reading the crash reports  
1. You need crash report - AppName.crash file 
2. AppName.dSYM file - which is generated with the build. The .dSYM should match which the build which is crashed.(You can find .dSYM file in build folder with the application)
Type the command below in Terminal for reading crash 
atos -o /MyApp.dSYM/Contents/Resources/DWARF/MyApp -arch armv7 0x00088d9c
1.Type atos -o in the terminal 
2.Right click on the MyApp.dSYM file then select "Show Package Contents" then drag and drop the file in /MyApp.dSYM/Contents/Resources/DWARF/MyApp to the terminal 
3. Type -arch armv7. (type armv6 if you are using old devices <iPhone4)  
4. Open .crash file and enter the address(refer fig) and press enter
5. It will display the function name which is crashed
6. Follow the same steps for remaining addresses(the addresses below the arrow mark in image) to get the sequence.

Removing all .svn files in a folder
find "/YourFolder" -name ".svn" -exec rm -fdR {} \;

Checking the distribution build
1. codesign -dvvvv yourAppName
2. Checking the entitlements : codesign -dvvvv --entitlements - /YourApp.app
          Check whether the Authority is Distribution
          Authority=iPhone Distribution:yourCompany, Inc
          Authority=Apple Worldwide Developer Relations Certification Authority
          And also for proper working of push notification check whether 'aps-environment' is 'production'