iOS2012. 9. 3. 17:21

UIImageView를 사용할 경우 UIImageView의 프레임 사이즈에 맞춰 이미지 비율에 따라 이미지 사이즈를 쉽게 재조정할 수 있지만

뷰상에서 특정 UIImage를 drawRect하는 경우에는 비율에 맞춰서 이미지 사이즈나 스케일을 재조정하는게 쉽지 않다.

따라서 다음과 같은 UIImage의 카테고리를 사용해 이미지 비율에 따라 UIImage스케일을 재조정한다.



카테고리의 헤더 파일 

#import <Foundation/Foundation.h>


@interface UIImage (UIImageSizeExtention)


- (UIImage *)fixImageSize;

- (UIImage *)fitToSize:(CGSize)newSize;

- (UIImage *)scaleToSize:(CGSize)newSize;

- (UIImage *)scaleProportionlyToWidth:(CGFloat)width;

- (UIImage *)scaleProportionlyToHeight:(CGFloat)height;

- (UIImage *)cropToRect:(CGRect)newRect;


@end   


카테고리의 실행 파일

#import "UIImage+SizeExtention.h"


@implementation UIImage (UIImageSizeExtention)
 

#pragma mark -

#pragma mark Basic


- (UIImage*)fixImageSize 

{

    // Fix some strange bug

    UIGraphicsBeginImageContext(self.size);

    [self drawInRect:CGRectMake(00self.size.widthself.size.height)];

    UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return newImage;

}


#pragma mark -

#pragma mark Fit (Combination)


- (UIImage *)fitToSize:(CGSize)newSize {

    float originalProportion = self.size.width/self.size.height;

    float targetProportion = newSize.width/newSize.height;

    float scaleProportion = newSize.width/self.size.width;

    UIImage *targetImage;

    

    if (targetProportion == originalProportion) {

        // Same Proportion

        // Do not have to crop, Direct scale

        targetImage = [self scaleToSize:newSize];

    } else if (targetProportion) {

        // Relative Landscape

        // Crop Rect

        CGFloat originX = self.size.width*scaleProportion/2 - newSize.width/2;

        CGRect cropRect = CGRectMake(originX, 0, newSize.width, newSize.height);

        // Scale to Height, Crop

        targetImage = [[self scaleProportionlyToHeight:newSize.heightcropToRect:cropRect];

    } else {

        // Relative Portrait

        // Scale to Width

        CGFloat originY = self.size.height*scaleProportion/2 - newSize.height/2;

        CGRect cropRect = CGRectMake(0, originY, newSize.width, newSize.height);

        targetImage = [[self scaleProportionlyToWidth:newSize.widthcropToRect:cropRect];

    }

   

   return targetImage;

}

               

#pragma mark -

#pragma mark Scale

               

- (UIImage *)scaleToSize:(CGSize)newSize {

    UIImage *targetImage = [self fixImageSize];

    // Prepare new size context

    UIGraphicsBeginImageContext(newSize);

    // Get current image

    CGContextRef context = UIGraphicsGetCurrentContext();

   

    // Change the coordinate from CoreGraphics (Quartz2D) to UIView

    CGContextTranslateCTM(context, 0.0, newSize.height);

    CGContextScaleCTM(context, 1.0, -1.0);

    // Draw (Scale)

    // The size of this drawRect is for scale

    CGRect drawRect = CGRectMake(00, newSize.width, newSize.height);

    CGContextDrawImage(context, drawRect, targetImage.CGImage);

   

    // Get result and clean

    UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

   

    return scaledImage;

}


- (UIImage *)scaleProportionlyToWidth:(CGFloat)width {

    float originalProportion = self.size.width/self.size.height;

    CGFloat height = width/originalProportion;

    return [self scaleToSize:CGSizeMake(width, height)];

}


- (UIImage *)scaleProportionlyToHeight:(CGFloat)height {

    float originalProportion = self.size.width/self.size.height;

    CGFloat width = height*originalProportion;

    return [self scaleToSize:CGSizeMake(width, height)];

}

               

#pragma mark -

#pragma mark Crop

               

- (UIImage *)cropToRect:(CGRect)newRect {

    UIImage *targetImage = [self fixImageSize];

    // Prepare new rect context

    UIGraphicsBeginImageContext(newRect.size);

    // Get current image

    CGContextRef context = UIGraphicsGetCurrentContext();

   

    // Change the coordinate from CoreGraphics (Quartz2D) to UIView

    CGContextTranslateCTM(context, 0.0, newRect.size.height);

    CGContextScaleCTM(context, 1.0, -1.0);

    // Draw (Crop)

    // This drawRect is for crop

    CGRect clippedRect = CGRectMake(00, newRect.size.width, newRect.size.height);

    CGContextClipToRect(context, clippedRect);

    CGRect drawRect = CGRectMake(newRect.origin.x*(-1), newRect.origin.y*(-1), targetImage.size.width, targetImage.size.height);

    CGContextDrawImage(context, drawRect, targetImage.CGImage);

   

    UIImage *croppedImage = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return croppedImage;

}    

@end



위의 카테고리를 다음과 같이 사용하였다.

UIImage *image = [UIImage imageNamed:/*Image name*/];


if
 (image.size.width > image.size.height

{

// If the width of a image is longer than the height
// rescale to width

image = [image scaleProportionlyToWidth:/*Image width*/];

}

else

{        

// If the height of a image is longer than the width
// recale to height

image = [image scaleProportionlyToHeight: /*Image height*/];

}


CGRect drawRect = CGRectMake(0, 0, image.size.width, image.size.height);


[image drawInRect:drawRect];


Posted by 다오나무
iOS2012. 9. 3. 14:52

iOS5 업데이트 후 이전에 만들어 놓았던 어플중 Navigation Bar가 초기화 되어서 보여지는 현상을 발견했다.

기존에 대부분의 개발자분들이 사용하던 방법은 UINavigationBar 라는 Class의 Category 형태로 만들어
UIView의 drawRect: 를 override해서 적용하던 방식이였던걸로 알고 있다.

그러나 iOS5로 업데이트 후 위에 명시한 UINavigationBar Category에서 명시한 drawRect 메서드가 호출되지 않는다.
그래서 애플 공식 문서에서 UINavigationBar에 대해서 찾아보았더니 아래와 같은 내용이 보인다.

Prior to iOS v5.0, when used in conjunction with a navigation controller, there are only a handful of direct customizations you can make to the navigation bar. Specifically, it is alright to modify the barStyletintColor, and translucent properties, but you must never directly change UIView-level properties such as the frameboundsalpha, or hidden properties directly. In addition, you should let the navigation controller manage the stack of navigation items and not attempt to modify these items yourself.

In iOS v5.0 and later, you can customize the appearance of the bar using the methods listed in “Customizing the Bar Appearance.” You can customize the appearance of all navigation bars using the appearance proxy ([UINavigationBar appearance]


잘은 모르겠지만.. UINavigation Controller Class에서 UIView-level properties를 직접 변경하지 못한다(??) 라고 적혀 있는 것 같다. 그렇다면 당연히 drawRect로 호출하지 못하는게 당연하다는 결론이 나온다.

해결방법은..
따라서 애플 공식 문서에서 말한 방법으로 “Customizing the Bar Appearance.” 해결 할 수 있다.

(1) iOS5 이하 버젼에서는 기존과 같은 방식으로 Category를 사용하면 되고
(2)  iOS5 이상 버젼에서는 아래와 같은 방식으로 처리한다. (iOS5 이하에서는 실행되지 않는다.)

if([self.navigationController.navigationBar respondsToSelector:@selector(setBackgroundImage:forBarMetrics:)] ) {

        NSLog(@"iphone 5 이상");

        //iOS 5 new UINavigationBar custom background

        UIImage *image = [UIImage imageNamed:@"이미지명"];

        [self.navigationController.navigationBar setBackgroundImage:image forBarMetrics:UIBarMetricsDefault];

    }  



* setBackgroundImage:forBarMetrics:

Sets the background image for given bar metrics.

- (void)setBackgroundImage:(UIImage *)backgroundImage forBarMetrics:(UIBarMetrics)barMetrics

Parameters

backgroundImage

The background image to use for barMetrics.

barMetrics

A bar metrics constant.

Availability

  • Available in iOS 5.0 and later.

Declared In

UINavigationBar.h

Posted by 다오나무
iOS2012. 9. 3. 13:28

For Gigster’s navigation concept it was necessary to implement a custom styled tab bar. Basically the screen is divided into three parts:

  • Navigation Bar
  • Tab Bar
  • Content

The navigation bar has a custom background image and a fixed title with custom font. The tab bar should be directly under the navigation bar and has complete custom styling.

Basic Setup

To start off I created a new Xcode project and added a UITabBarController with three static views. Although I have XIBs for each content view, I added everything else in code. Each view has a UINavigationController so it can easily contain its own navigation stack.

Navigation Bar

An easy technique to get a custom background in a UINavigationBar is to make a cateogory. Add this code (e.g. in YourAppDelegate.m):

@interface UINavigationBar (MyCustomNavBar)
@end
@implementation UINavigationBar (MyCustomNavBar)
- (void) drawRect:(CGRect)rect 
{
    UIImage *barImage = [UIImage imageNamed:@"nav_bg.png"];
    [barImage drawInRect:rect];    
}
@end

To get the fixed title with custom font I added a method that creates a UILabel:

- (UILabel *)_makeTitleLabel
{
    CGRect frame = CGRectMake(0032044);
    UILabel *label = [[[UILabel alloc] initWithFrame:frame] autorelease];
    label.backgroundColor = [UIColor clearColor];
    label.font = [UIFont fontWithName:@"Marker Felt" size:24];
    label.shadowColor = [UIColor colorWithWhite:0.0 alpha:0.5];
    label.textAlignment = UITextAlignmentCenter;
    label.textColor = [UIColor whiteColor];
    label.text = @"Custom Tab Bar";
    
    return label;
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    ...
    // add static title labels
    [tab1NavigationController.navigationBar addSubview:[self _makeTitleLabel]];
    [tab2NavigationController.navigationBar addSubview:[self _makeTitleLabel]];
    [tab3NavigationController.navigationBar addSubview:[self _makeTitleLabel]];
    ...
}

Tab Bar

A little bit of frame hackery was necessary to get the tab bar right beneath the navigation bar:

    // disable autosizing of tabbar and move it to correct position
    tabBarController.tabBar.autoresizingMask = 0;
    tabBarController.tabBar.frame = CGRectMake(04432065);
    
    
    // fix frame of tabbarcontroller's view
    CGRect frame = tabBarController.view.frame;
    frame.size.height += 29;
    frame.origin.y = 20;
    tabBarController.view.frame = frame;

To have a custom layout for the tab bar, it is necessary to subclass UITabBarController. We don’t need UIKit’s tab bar at all, so we just hide it and create our own custom tabs:

- (void)viewDidLoad
{
    // find the normal tab bar and hide it
    for(UIView *view in self.view.subviews)
    {
        if([view isKindOfClass:[UITabBar class]])
        {
            view.hidden = YES;
            break;
        }
    }
    
    [self _setupCustomTabBar];
}

First I added a UIImageView containing a background image:

- (void)_setupCustomTabBar
{
    // background image
    self.bgImageView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:@"tabs_bg.png"]] autorelease];
    bgImageView.frame = CGRectMake(04432065);
    [self.view addSubview:bgImageView];

Next we want to display a tab image to indicate which tab is active:

    self.contentView = [[[UIView alloc] initWithFrame:CGRectMake(04464065)] autorelease];
    [self.view addSubview:self.contentView];
    
    // sliding tab image
    self.tabImage = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:@"tab.png"]] autorelease];
    CGRect frame = self.tabImage.frame;
    frame.origin = CGPointMake(BUTTON_X_OFFSET, BUTTON_Y);
    self.tabImage.frame = frame;
    [self.contentView addSubview:self.tabImage];

Finally I added three buttons to trigger tab selection:

    // Make custom tab buttons and add them to the content view
    [self.contentView addSubview:[self _makeTabButtonWithTitle:@"Tab 1" atIndex:0]];
    [self.contentView addSubview:[self _makeTabButtonWithTitle:@"Tab 2" atIndex:1]];
    [self.contentView addSubview:[self _makeTabButtonWithTitle:@"Tab 3" atIndex:2]];
}

Button creation method:

- (UIButton *)_makeTabButtonWithTitle:(NSString *)title
                              atIndex:(NSInteger)index
{
    UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
    button.frame = CGRectMake(BUTTON_X_OFFSET + index*BUTTON_WIDTH, BUTTON_Y, BUTTON_WIDTH, BUTTON_HEIGHT);
    button.tag = index;
    button.titleLabel.font = [UIFont fontWithName:@"Marker Felt" size:18];
    button.titleLabel.shadowColor = [UIColor colorWithWhite:0 alpha:0.5];
    button.titleLabel.shadowOffset = CGSizeMake(0-1);
    [button setTitle:title forState:UIControlStateNormal];
    [button addTarget:self action:@selector(_buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
    
    return button;
}

Note that the buttons’ action is _buttonClicked. There we need to set the currently selected tab index in the UITabBarController.

- (void)_buttonClicked:(id)sender
{
    self.selectedIndex = [sender tag];
    [self _updateTabImage];
}

We also want to update the tab image’s position after a tab was selected. This is what _updateTabImage is for:

- (void)_updateTabImage
{
    CGRect targetRect = CGRectMake(BUTTON_X_OFFSET + self.selectedIndex*BUTTON_WIDTH, BUTTON_Y, BUTTON_WIDTH, BUTTON_HEIGHT);
    
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.3];
    [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
    
    [self.tabImage setFrame:targetRect];
    
    [UIView commitAnimations];
}

Now we have a fully functioning tab bar with custom layout. You can download the full Xcode project here:MyCustomTabBar_final.zip.

Gigster in the App Store: Jump

Posted by 다오나무
iOS2012. 8. 29. 17:04

맥이나 iOS에서 소켓 프로그래밍을 구현하기 위해 직접 BSDSocket 혹은 CFSocket을 사용해서 만드는 방법이 있습니다. 그러나 저처럼 처음부터 쌓기 싫어하는 사람을 위해서 잘 포장된 라이브러리가 있는데 그것이 바로 CocoaAsyncSocket입니다.

https://github.com/robbiehanson/CocoaAsyncSocket

예전에는 구글 코드에 있었는데 현재는 github에서 진행되고 있습니다. 오픈소스이고, 간혹 버그가 생기는 경우도 있습니다만 (직접 경험) 개발자에게 알려주니 며칠 뒤에 고쳐져 있더군요.


이 라이브러리의 사용법은 생각외로 간단합니다.


다운 받는 방법


다른 프로그램을 사용할 필요 없이 해당 버튼만 누르면 zip 형태로 받을 수 있습니다. 


압축을 풀면 폴더와 파일이 나옵니다. 다른 폴더는 신경쓰지 않고, 소켓 라이브러리만 본다면 두 폴더로 나눌 수 있습니다
RunLoop : AsyncSocket(.h/.m) AsyncUdpSocket(.h/.m)
GCD : GCDAsyncSocket(.h/.m) GCDAsyncUdpSocket(.h/.m)

tcp와 udp는 보면 구별가실테고, GCD는 모르겠으면 검색해 봅시다.
런루프 폴더의 라이브러리는 업데이트가 중단된지 꽤 되었습니다. 안정성도 보장할 수 없습니다. 코드를 보고 어떤 구조인지 파악하기에는 문제 없을지도 모르나, 실 사용에서는 권장하지 않습니다.
GCD 폴더에는 GCD를 사용해 thread-safe하게 설계되었습니다. 실제 사용할 목적이라면 이 쪽을 사용하도록 합시다.


기본적인 사용 방법은 아주 간단합니다. 그냥 프로젝트에 해당 파일을 넣으면 됩니다. 가령 UDP 소켓 통신을 원한다면 GCDAsyncUdoSocket라는 이름의 두 파일만 추가해서 사용하면 됩니다.

예제는 폴더 안에 들어있는 예제들을 참고하시면 됩니다.


라고 하고 끝나면 부실하니 간단하게 사용하는 설명을 해보죠. 저는 UDP를 구현한지라 UDP를 중심으로 설명하겠습니다. TCP와 큰 차이는 없을테니 다른 포함된 예제를 보시고 비교하면서 공부해보도록 합시다.


사용하기 전에 우선 헤더 파일에 해당되는 GCDAsyncUdpSocket.h를 해당 헤더 파일에 포함시킵니다.
그리고 소켓을 선언해 주셔야 되는데 대략 이런 방식으로 하시면 됩니다.

@interface UDPSocket : UIViewController {

    long tag;
    GCDAsyncUdpSocket *udpSocket;

}

long 타입은 특별한 경우가 아니면 하나로도 충분하고, 소켓의 경우에는 필요에 따라서 여러개를 사용할 수도 있습니다. 물론 여러개를 사용하려고 한다면 선언도 그에 맞춰서 해줘야겠죠.



소스 코드에서의 처리는 다음과 같습니다.

- (void) viewDidLoad() 
{
    //.......
    udpSocket = [[GCDAsyncUdpSocket allocinitWithDelegate:self delegateQueue:dispatch_get_global_queue(00)];
 
   
if (![udpSocket bindToPort:0 error:&error]) {
      NSLog(@"Error binding: %@", error);
       
return;
    
}

   if
 (![udpSocket beginReceiving:&error]
   {
      NSLog(@"Error receiving: %@", error);
       
return;
   }
}

viewDidLoad에서 구현하는 이유는 미리 선언을 해주어 이후에 사용하기 편하게 하기 위해서입니다.
딱히 따로 설명할 것은 없고, 코드를 보면 무슨 기능을 하는지 대충 감이 오리라 생각됩니다.


위 작업을 행한 다음에 실제로 데이터를 주고 받을때는 이렇게 합니다.
[udpSocket sendData:data toHost:host port:port withTimeout:0.5 tag:tag];

 data는 NSData형식입니다. 다른 데이터 전송 방식이 있는지는 잘 모르겠네요.
 host는 보낼 대상의 ip, port는 포트 번호입니다. 
 withTimeout은 타임아웃 시간 설정입니다. 0일때는 타임아웃을 하지 않고, 이외의 양수의 경우에는 해당 시간(초)만큼 기다려도 응답이 오지 않으면 그만두는거죠.

 
보내는 것은 위와 같이 하고, 받는 것은 아래와 같이 합니다.
TCP와 UCP가 다른 것은 이 부분입니다만, 약간의 함수명 차이만 있고 사용법은 비슷합니다.

- (void)udpSocket:(GCDAsyncUdpSocket *)sock didSendDataWithTag:(long)tag
{
}

- (void)udpSocket:(GCDAsyncUdpSocket *)sock didNotSendDataWithTag:(long)tag dueToError:(NSError *)error 
{
}

- (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data 
    fromAddress:(NSData *)address withFilterContext:(id)filterContext
{
}

UDP에서는 위의 3가지를 포함시켜야 합니다. 실제 구현내용이 없어도 되니 꼭 넣어주도록 합시다.

UDP의 특성상 보냈는지 확인하는 2개의 함수는 별 내용이 없어도 되더군요. TCP라면 신경써서 체크해줘야겠지만 말입니다.
세번째가 데이터를 받았을 때 처리하는 함수입니다.


 라이브러리가 쉬워서 별로 설명할건 없네요. 기본적인건 위 수준에서 원하는걸 조금 고치는 정도로도 충분합니다. 

Posted by 다오나무
iOS2012. 8. 28. 09:47

iOS5 이전에는 JSON API가 private API가 되어서 사용할 수 없었기 때문에, yajl, json-framework, jsonTouch 등 써드파티 json 라이브러리들을 사용해야했다. 써드파티 라이브러리이기 때문에 외부 라이브러들을 불러와아야했고, ilnk를 걸어줘야하는 등 개발에 필요한 것들이 많았었다. 하지만 iOS5에서 부터는 NSJSONSerialization API를 사용할수 있게 되었다. 

NSJSONSerialization은 다른 써더파티 라이브러리와 마찬가지로 NSDicationary와 NSArray로 형태의 객체로 바로 매핑할 수 있다. 또한 모든 객체는 NSString, NSNumber, NSArray, NSDictionary 또는 NSNull로 매핑된다. 그리고 모든 객체의 키이름은 NSString으로 사용된다.간단히 

NSJSONSerialization을 사용하는 방법에 대한 예제를 준비했다. 
{
   "name" : "saltfactory",
   "e-mail" : "saltfactory@gmail.com"
}
이라는 간단한 JSON을 파싱하는 테스트를 위해서 UnitTest로 준비했다.

JSON 문자열을 NSDictionary 객체로 매핑하기 위해서는 다음과 같은 단계가 필요하다. NSJSONSerialization에서 에러를 담을 NSError가 필요하기 때문에 NSError 변수를 하나를 만든다. 그리고 JSON문자열을 NSJSONSerialization에서 사용할 수 있는 NSData로 변형시켜야한다. 이렇게 준비된 data와 error를 JSONObjectWithData:options:error: 메소드를 이용해서 NSDictionary로 바로 매핑할 수 있다. 예제는 JSON문자열을 NSJSONSerialization을 이용해서 NSDictionary로 파싱해서 name이라는 키의 값과 @"saltfactory"라는 문자열이 같은지 단위테스트를 한 것이다. 결과는 올바르게 해석되어 매핑되었기 때문에 단위테스트가 성공적으로 끝나게 된다.

 - (void)testParsingJSON

{

    NSString *jsonString = @"{\"name\":\"saltfactory\",\"e-mail\":\"saltfactory@gmail.com\"}";

    NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];

    NSError *error;

    NSDictionary *jsonInfo = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&error];

    

    NSAssert([[jsonInfo valueForKey:@"name"] isEqualToString:@"saltfactory"], @"not equals");

}


다음은 NSDictionary의 객체로 JSON 문자열을 생성하는 방법이다. 이 방법은 JSON의 문자열을 NSDictionary로 매핑하는 방법과 반대로 실행하면 된다. 우선 키이름과 NSString, NSNumber, NSArray, NSDictionary, NSNull 중에 하나의 객체를 값으로 가지는 NSDictionary를 만든다. 그리고 그 NSDictionary 객체를 NSData로 생성하는데 이때 NSJSONSerialization의 dataWithJSONObject:options:error: 메소드를 사용해서 객체를 JSON data로 생성한다. 이때 옵션으로 NSJSONWritingPrettyPrinted라는 옵션을 사용했는데, 이 옵션을 사용한 이유는 JSON 문자열을 만들때 whitespace가 포함되어 가독석을 높이기 위해서 이 옵션을 사용했다. 마지막으로 이 data를 이용해서 NSString을 만들면 된다. 이렇게 생성된 JSON 문자열을 출력해보면 처음 테스트하려던 JSON과 동일한 형태의 JSON이 출력되는 것을 확인할 수 있다. 

- (void)testGeneratingJSON

{

    NSError *error;

    NSString *jsonInfo = [NSMutableDictionarydictionaryWithObjectsAndKeys:@"saltfactory",@"name",@"saltfactory@gmail.com",@"e-mail"nil];

    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonInfo options:NSJSONWritingPrettyPrintederror:&error];

    NSString *jsonString = [[NSString allocinitWithData:jsonData encoding:NSUTF8StringEncoding];

    NSLog(@"jsonString => %@", jsonString);


Posted by 다오나무
iOS2012. 8. 27. 11:00

As a newcomer to IOS programming this particular issue on my first app took a bit of working out so this is a tutorial for others struggling with the same issue.

The ultimate objective of this code is to get a out like shown below

Firstly things to note.

      UI components need to stay on the main thread, the main thread needs to perform the UI updates. (Cocoa Fundamentals Guide, page 164: “All UIKit objects should be used on the main thread only.”)
      This tutorial/code sample is about adding a UIActivityIndicatorView programmatically
      You will need #import adding to your code file
      in this code activityIndicator is a property

I am not entirely sure this is the best way as I have only been doing IOS for a short while but it works for me.

So I suggest

Firstly start by creating a method where you start the whole thing off (probably triggered from a button press- in this sample it is showNearby.

Secondly create a NSTimer and start it immediately calling the show activity selector

Thirdly start your marker/pin loading method using the performSelectorInBackground method.

Finally remove the indicator when you have completed your marker/pin showing / loading
That’s it you will have a activity indicator that appears whilst loading and is removed once done.

The code for this is shown here.

-(IBAction)showNearby:(id)sender
{
    
    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(ShowActivityIndicator) userInfo:nil repeats:NO];
    [[NSRunLoop mainRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];   

    [self performSelectorInBackground:@selector(loadMarkers) withObject: nil];
   
    
}

-(void)  ShowActivityIndicator
{
    
    activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge] ;
    CGRect frame = activityIndicator.frame;
    frame.origin = CGPointMake(290, 12);
    frame.size = CGSizeMake(100, 100);
   
    activityIndicator.backgroundColor=[UIColor blackColor]; 
    CALayer *layer =[activityIndicator layer];
    layer.cornerRadius = 8.0;
    activityIndicator.alpha=0.7;
    activityIndicator.frame = frame;
    activityIndicator.center = self.view.center; 
    activityIndicator.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
    activityIndicator.tag = 2;
    
    [self.view addSubview:activityIndicator];
    [self.view bringSubviewToFront:activityIndicator];  
    [activityIndicator startAnimating];
}

-(void) loadMarkers
{
  //code to load markers then remove activityIndicator

[activityIndicator stopAnimating];
[activityIndicator removeFromSuperview];
}

Posted by 다오나무
iOS2012. 8. 26. 13:02

안녕하세요. 팬텀입니다. 
첨부된 파일은 이번 정모에 발표했던 'App과 Server의 은밀한 대화'에 리뷰했던 코드 예제 입니다.
그날 리뷰했던 코드에 urlencoding / urldecoding 관련된 부분이 추가되었습니다.

첨부된 파일을 xcode에서 실행시키면, 뷰에 버튼이 하나 있습니다. 
이 버튼이 parameter와 security token을 만들고, 서버로 request를 보내고,
서버로부터의 결과를 콘솔에 남기는 일을 합니다. 콘솔에서 다음과 같이 확인할 수 있습니다.

 [Session started at 2010-07-21 00:57:01 +0900.]
 -------------------------------------------
[app]
1) param : id=2&point=450&name=%EB%A7%A5%EB%B6%80%EA%B8%B0&nonce=16807
2) st : PysueoNho2uhis%2B8%2FQjpWuSuYdGYA0m4HM969zadxBZIMNwe%2BJle
 -------------------------------------------
[server]
1) result : id=2&point=450&name=맥부기&nonce=16807

endpoint는 코드에 들어있는 http://rockk.org/mcbugi/test.php 입니다. 2주 정도 열어 놓도록 할께요.
서버쪽 코드는 다음과 같구요. 간단하게 st를 복호화 한 값을 그대로 echo 합니다.
app과 key값을 꼭 맞춰주어야 합니다. 아래 붉게 강조된 부분에요. '123456789abcdef'

 <?php
// base64 decode st
$tmp_st = base64_decode(urldecode($_POST['st']));

// decrypt st (key is '123456789abcdef')
$st = urldecode(trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, '123456789abcdef', $tmp_st, MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB), MCRYPT_RAND))));

// echo decrypted data
echo $st;
exit;
?>


실제로 사용할 땐, $st 값을 &로 파싱하고, key & value로 활용하면 되지 않을까 싶습니다.

CryptoHelper 관련 참고 url은 아래와 같구요.
http://pastie.org/297563.txt
http://stackoverflow.com/questions/1235171/iphone-how-to-encrypt-a-string

php의 mcrypt와 padding을 다루는 부분에서 차이가 있어CryptoHelper.m의 다음 부분에 kCCOptionECBMode를 추가했습니다.
참고하세요.

 // Create and Initialize the crypto reference.
    ccStatus = CCCryptorCreate(    encryptOrDecrypt, 
                               kCCAlgorithmAES128, 
                               kCCOptionPKCS7Padding | kCCOptionECBMode
                               (const void *)[theSymmetricKey bytes], 
                               kCCKeySizeAES128, 
                               (const void *)iv, 
                               &thisEncipher
                               );


혹시나 해서 발표자료도 공유합니다- :D

App과 Server의 은밀한 대화
View more presentations from rockk.




한글 잘림현상- 아래수정 부분 확인해서 사용하삼
- (NSString*)encryptString:(NSString*)string
{
NSRange fullRange;
fullRange.length = [string length];
fullRange.location = 0;

uint8_t buffer[[string length]];

[string getBytes:&buffer maxLength:[string length] usedLength:NULL encoding:NSUTF8StringEncoding options:0 range:fullRange remainingRange:NULL];

// NSData *plainText = [NSData dataWithBytes:buffer length:[string length]];// 한글 잘림
NSData *plainText = [string dataUsingEncoding:NSUTF8StringEncoding];

NSData *encryptedResponse = [self doCipher:plainText key:symmetricKey context:kCCEncrypt padding:&pad];

return [self base64EncodeData:encryptedResponse];
}

Posted by 다오나무
iOS2012. 8. 22. 11:46

http://sonnyparlin.com/2011/12/pulltorefresh-ios-5-and-arc-tutorial/

Posted by 다오나무
iOS2012. 8. 22. 09:56

아이폰이나 안드로이드 프로젝트를 하다보면 처음에 가장 신경쓰이는 부분이 UI구성입니다.

디자인에 따라 배경이나 버튼 이미지, 간격등을 고려해서 구성해야 되는데 책이나 기본 강좌에서는 이런 내용을 잘 다루지 않죠.
기획안에 따라서 기능적으로는 UINavigationController를 사용해야 되는데 화면의 특성에 따라서 UINavigationController를 숨기거나 수정해야되는 경우가 생깁니다. 

UINavigationController에서 배경색을 바꾸고 배경이미지를 넣고 상황에 따라 타이틀을 바꾸고 하는 등의 작업을 간단히 정리해 보도록 하겠습니다.

1. 코드를 이용해 UINavigationController 만들기
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {     
 RootViewController * rootViewController = [[RootViewController alloc] initWithNibName:@"RootViewController" bundle:nil];

 UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:rootViewController];

 [self.window addSubview:navigationController.view];
 [self.window makeKeyAndVisible];
 return YES;
}
이것은 코드를 통해 만들수 있다는 한가지 예일 뿐이고 실제 interface builder를 이용해서 만드셔도 상관 없죠.
 
2.Title 바꾸기
- (void)viewDidLoad 
{
    [super viewDidLoad];
    self.title = @"My Title";
}
화면별로 각각의 ViewController에 따라서 Title을 변경해야 될 떄 사용 되겠죠.

3.Navigation Bar 숨기기 & 보이기
- (void)viewDidLoad 
{
    [super viewDidLoad];
     self.title = @"My Title";
     self.navigationController.navigationBarHidden = YES; //YES : 숨기기, NO : 보이기
}
위 코드는 Navigation Bar를 숨기는 코드 입니다. 이처럼 ViewController에 따라서 보여줄수도 있고 숨길수 있습니다.
다시 보이게 하려면 self.navigationController.navigationBarHidden값을 NO로 하시면 되겠습니다.

4. Navigation Bar 배경색 바꾸기 (Background Color) 
- (void)viewDidLoad 
{
    [super viewDidLoad];
    self.title = @"My Title";
    self.navigationController.navigationBar.tintColor = [UIColor colorWithRed:255.0/255 
                                                                                  green:10.0/255 blue:100.0/255 alpha:1];

}

5. Navigation Bar Style 바꾸기
- (void)viewDidLoad 
{
   super viewDidLoad];
   self.title = @"My Title";
   self.navigationController.navigationBar.barStyle = UIBarStyleBlack; //스타일 적용
   self.navigationController.navigationBar.translucent = YES; // 반투명 효과 주기
}
스타일에는 기본적으로 UIBarStyleBlack, UIBarStyleBlackOpaque, UIBarStyleBlackTranslucent, UIBarStyleDefault이 있죠.

6. Navigation Bar 배경이미지 넣기 (Background image)
@implementation UINavigationBar (UINavigationBarCategory)
- (void)drawRect:(CGRect)rect
{
   UIImage *bgImage = [UIImage imageNamed:@"background.png"];
   [bgImage drawInRect:rect];
}
@end
UInavigationBar 서브클래스를 만들어서 구현합니다.

7. Navigation Bar Back 버튼의 Text 변경 하기
- (void)viewDidLoad 
{
    [super viewDidLoad];
    self.title = @"My title";
    self.navigationController.navigationBar.backItem.title = @"뒤로가기";
}
본래 "Back"이라는 Text가 들어가는데 "뒤로가기" 라고 바꿔 주었습니다.
위 코드는 상황에 따라 적용되지 않을 수 있습니다. 
가령 UITabbarController와 UINavigationController를 같이 사용하면서 pushViewController를 이용해 뷰를 이동할 때 안되더군요.

이때는 다음과 같이 해결 합니다.
UIBarButtonItem *backButton = [[UIBarButtonItem alloc]
                                    initWithTitle: @"Back"
                                    style: UIBarButtonItemStyleBordered
                                    target:nil action:nil];
[[self navigationItem] setBackBarButtonItem: backButton];

UINavigationController *homeNav = self.navigationController;
LoginView * loginView = [[LoginView alloc] initWithNibName:@"LoginView" bundle:nil];
[homeNav pushViewController:loginView animated:YES];

위 코드는 이동하고자 하는 뷰(LoginView)를 push하는 뷰 컨트롤러에서 백버튼을 재설정한 후에 뷰를 push하는 방법 입니다.
즉 뷰를 이동하고 나서 처리하는 것이 아니라 이동하기 전에 백버튼을 설정하고 이동하는 처리 입니다.

여기서 하나 더 알아봅시다.
NavigationController에서 화면 전환시 이전 화면으로 이동하고자 할 때 바로 이전 화면이 아닌 자신이 원하는 이전 화면으로 이동하고 싶을 때 처리는 어떻게 할까요. 

다음을 보시죠
[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:0] animated:YES];
위처럼 objectAtIndex값을 설정해서 이전의 원하는 컨트롤러로 이동할 수 있습니다.

8.Navigation Bar 오른쪽의 버튼 아이템 추가하기
- (void)viewDidLoad 
{
    [super viewDidLoad];
    self.title = @"My Title";
    UIBarButtonItem *loginButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Log in" 
                                                            style:UIBarButtonItemStylePlain target:self  

                                                            action:@selecter(ActLogin)];
    self.navigationItem.rightBarButtonItem = loginButtonItem;
    [loginButtonItem release];
}

//버튼을 눌렀을 때 이벤트 처리
-(IBAction) ActLogin:(id)sender
{
     NSLog(@"Log-in Action");
}
Navigation Bar 오른쪽 영역에 버튼을 추가하는 코드 입니다. 여기서는 버튼을 눌렀을 때 이벤트 메소드 ActLotin 메소드도 정의 했습니다.
버튼의 종류나 여러가지 옵션은 상황에 맞게 처리하시면 되겠습니다.

일단 NavigationController 커스터마이징은 여기까지 정리해 놓겠습니다. 
추가적으로 필요한 내용은 주기적으로 업데이트 하겠습니다.

Posted by 다오나무
iOS2012. 8. 20. 17:11

copyright www.irontop.com All rights reserved.

This Example show to you that communication with iPhone App, iPhone UIWebView each other 
There is two ways for commucation.
One is the [webView:shouldStartLoadWithRequest:navigationType:]UIWebViewDelegate 
The other is the [stringByEvaluatingJavaScriptFromString:] WebView

You can use "shouldStartLoadWithRequest" to commucate UIWebView 2 APP
You can use "stringByEvaluatingJavaScriptFromString" to commucate APP 2 UIWebView 

유관업체에서 앱과 웹뷰간의 통신 예제를 작성해달라고 해서.
간단히 만들어 보았다.

웹뷰에는 자바스크립트의 머리를 깎아줄 수 있는 api를 제공하기 때문에(eval)
자바스크립트를 잘 이해하고 있다면, 팝업을 포함하여 거의 모든 기능을 훼이크로 구현이 가능하다.

아래는 주요 코드 부분이다.

APP에서 WebView로 값을 보내는 부분

 71 (IBAction)toWeb:(id)sender {
 72     NSString *strScript = [NSString stringWithFormat:
 73                            @"var fromApp = document.getElementById('fromApp');\
 74                            fromApp.value = '%@';"self.txtToWebView.text];
 75     [webView stringByEvaluatingJavaScriptFromString:strScript];
 76 }


input 요소를 id로 찾아서 값을 세팅하는 자바스크립트를 생성한 후에
webView에 밀어 넣고 있다.

WebView로부터 APP로 보내지는 값을 획득하는 부분
 83 (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
 84     
 85     NSString *strUrl = [[request URL] absoluteString];
 86     if ([strUrl hasPrefix:@"toAPP://"]) {
 87         NSString *strRequest = [[strUrl componentsSeparatedByString:@"://"] objectAtIndex:1];
 88         NSArray *arrRequest = [strRequest componentsSeparatedByString:@"?"];
 89         NSString *strCmd = [arrRequest objectAtIndex:0];
 90 
 91         if (YES == [@"toAppCmd" isEqualToString:strCmd]) {
 92             // code to set Your Application communication
 93             self.txtFromWebView.text = [arrRequest objectAtIndex:1];
 94         }
 95         
 96         // toApp protocol do not StartLoad
 97         return NO;
 98     }
 99     
100     // other protocol do StartLoad
101     return YES;
102 }


form GET Action을 사용하였고,
action-url을 "toApp://toAppCmd?toApp=abc" 형식이 되도록 하였다.
즉, 로드할 url을 가로채서 "toAPP" 로 시작하는 경우에는 NO 를 리턴하여 실제로 로드되지 않도록 하고,
url을 파싱하여 toApp 의 값을 획득하도록 하였다.
여기서 strCmd 를 명령으로 사용하는 예제이므로 toAppCmd 를 다른 값으로 바꾸고,
구분하여 처리하는 것으로 처리할 종류(명령)을 구분할 수 있는 구조로 확장도 가능하다.

자세하는 것은 첨부된 예제 파일을 참조하자.~~

Posted by 다오나무