티스토리 툴바

The Categorized OpenGL ES Tutorial Collection

  
저작자 표시

'OpenGL' 카테고리의 다른 글

The Categorized OpenGL ES Tutorial Collection  (0) 2012/01/20
OpenGL 스터디 자료  (0) 2011/12/15
OpenGL Data Type  (0) 2011/12/09
OpenGL ES 관련 블로그  (0) 2010/04/12

UISearchBar 커스텀

Posted 2012/01/20 00:31

- (void)viewDidLoad

{

    [super viewDidLoad];

    UISearchBar *customSearchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];

    [customSearchBar setPlaceholder:@"종목명 검색"];

    

    // 서치바의 기본 백그라운드 레이어 제거.

    if ([[[customSearchBar subviews] objectAtIndex:0] isKindOfClass:[UIImageView class]])

    {

        [[[customSearchBar subviews] objectAtIndex:0] removeFromSuperview];

    }

    

    // 검색 아이콘 제거. 이미 백그라운 레이어를 제거했기 때문에 objectAtIndex = 0.

    UITextField *textField;

    if ([[[customSearchBar subviews] objectAtIndex:0] isKindOfClass:[UITextField class]])

    {

        textField = [[customSearchBar subviews] objectAtIndex:0];

    }

    textField.leftView = nil;

    

    // 텍스트필드의 프레임 변경.

    UIImage *searchBarImage = [[UIImage imageNamed:@"inputbox.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(6, 7, 6, 7)];

    [textField setBackground:searchBarImage];

    

    // 텍스트필드의 폰트와 사이즈 변경.

    [textField setFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:15]];

    

    // 서치바에 백그라운드 추가.

    [customSearchBar setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:@"inputbox_bg.png"]]];

    [[customSearchBar layer] setOpaque:NO];

    

    [self.view addSubview:customSearchBar];

    [customSearchBar release];

}


[참고] iOS 5 : UIImage and resizableImageWithCapInsets
저작자 표시
하도 오래되어서 정확한 버전과 년도는 생각나지 않지만, 이클립스를 사용하면서 가장 반가웠던 기능 중의 하나가 자동으로 getter/setter을 생성해 주는 것이었다. 완전히 같거나 이클립스 만큼 편하지는 않지만(프라퍼티 생성의 경우) Xcode 4에서도 프라퍼티와 액션과 관련된 코드를 자동으로 생성해주는 기능이 추가되었다.

우선 기능을 사용해 보기 위해, Xcode에서 Test라는 이름의 프로젝트를 Single View Application 템플릿을 이용해 생성한다. 그리고 xib 파일을 열어 다음과 같이 화면 설정을 하자.

 
그 다음, Assistant Editor을 열자.

 
위 그림과 같은 상태에서 라벨을 선택한 후 마우스 오른쪽 버튼을 클릭한 후  Assistant Editor의 프라터티를 작성할 위치로 드래그 해보자. 그러면 다음 그림과 같이 작업 팝업창(insertion maker)이 하나 뜰 것이다.

 
Name 항목에 이름을 입력하면 Connect 버튼이 활성화 된다. 클릭하자. 그러면 해당 위치에 다음과 그림과 같이 코드가 생성되는 것을 볼 수 있을 것이다.
물론 구현 파일에도 신써사이즈 코드가 추가되었음은 물론... UI와 관련된 객체들의 프라퍼티/씬서싸이즈는 이 방식을 이용하면 된다.


자, 다음은 액션(IBAction)과 관련된 코드를 자동으로 추가해 보자.
UIButton은 선택한 상태에서 Xcode의 유틸리티 영역(오른쪽)에서 Connection inspectior를 선택한다. 그리고 Touch Up Inside 항목을 선택한 후 Assistant Editor로 드래그 한다. 나머지는 UILabel의 프라터티를 추가하는 방법과 유사하다. 그러면 헤더와 구현 파일에 액션과 관련된 메서드가 추가될 것이다.
액션이 필요한 객체는 UILabel과 같은 방식으로 Connection에 Action 옵션을 선택하여 메서드를 추가할 수도 있다.



 
저작자 표시

2012년 새해 복 많이 받으세요.

Posted 2011/12/29 17:20



저작자 표시

초간단 그리드뷰

Posted 2011/12/28 00:05
iOS에서 기본적으로 제공하는 객체 중 가장 활용도가 높은 것은 단연 UITableView이다. 본연의 기능인 데이터 출력 뿐만 아니라 화면의 레이아웃 작업을 좀더 쉽게 도와준다. UITableView의 기본적인 내용과 다양한 활용법만으로도 책 한권의 분량은 족히 된다.

그러나 불만이 딱 한 가지가 있다, 다른 개발자들도 이에 동의하리리 믿는다, 단일 컬럼만 지원한다는 점이다. 물론 UITableViewCell을 커스텀 하여 다중 컬럼을 사용할 수는 있다. 그렇지만 실제 현업에서는 생각보다 복잡하다. 특별한 경우에는 엑셀의 시트와 같은 기능과 UI를 요구하기도 한다. 이 부분은 향후에라도 애플에서 직접 API를 제공했으면 하는 바램이다.(애플이 아이폰과 같은 작은 화면에 이런 것이 필요 없다고 생각할 지라도...)

개인적인 생각으로는 왜 작은 화면에 컴퓨터의 모니터에나 적합한 UI를 굳이 사용하려는지 이해할 수 없지만, 클라이언트의 무조건적인 요구로 인해 실제 비슷하게 개발을 하게 된다. 이런 경우 절실하게 필요한 것이 Grid이다. 구글링을 해보면 Grid를 구현한 다양한 오픈소스를 구할 수 있다. 심지어는 유로 소스도 존재한다. 우선 몇 가지를 소개한다.


일과 관련하여 그리드와 비슷한 UI가 필요하여 위에서 소개한 오픈소스의 사용을 먼저 검토해 보았다. 오픈소스의 그리드는 스크롤뷰와 UITablViewDataSource와 같은 패턴을 사용하고 있다. 잠시 후 살펴 보겠지만, 실제 필요한 것은 엑셀의 시트와 같은 출력만 필요할 뿐이어서 직접 구현하기로 했다. 물론 진정한 Grid에서 제공하는 다양한 기능은 없지만 필요한 것은 단지 출력을 위한 것이어서 UITableView를 이용하기로 했다.

UI의 요구사항은 다음과 같다.
 


 
요구사항을 만족하기 위해 두개의 테이블뷰와 하나의 스크롤뷰를 사용하기로 했다. 그외 디자인 요구사항을 위해 UITableView에 커스텀 헤더와 셀을 적용했다. 자 그럼 시작해 보자. 우선 Xcode의 템플릿에서 Single View Application을 선택하 SimpleGridView라는 이름의 프로젝트를 생성한다. 그리고 생성된 프로젝트에 UIView를 상속하는 LPTableHeader라는 클래스를 생성한다. 그리고 다음과 같이 수정하자.

[LPTableHeader.h]

#import <UIKit/UIKit.h>


@interface LPTableHeader : UIView


- (id)initWithFrame:(CGRect)frame andTitles:(NSArray *)titles;


@end

 
[LPTableHeader.m]

#import "LPTableHeader.h"

#import <QuartzCore/QuartzCore.h>


#define kTitleLabelWidth 100


@implementation LPTableHeader


- (id)initWithFrame:(CGRect)frame andTitles:(NSArray *)titles;

{

    self = [super initWithFrame:frame];

    if (self

    {

        // 테이블뷰 헤더의 그림자 효과.

        self.layer.shadowColor = [[UIColor darkGrayColor] CGColor];

        self.layer.shadowOffset = CGSizeMake(1.0, 1.0);

        self.layer.shadowOpacity = 0.40;

        

        for (int i = 0; i < [titles count]; i++) 

        {

            CGRect titleRect = CGRectMake(i * kTitleLabelWidth, 0, kTitleLabelWidth, frame.size.height);

            UILabel *label = [[UILabel alloc] initWithFrame:titleRect];

            label.backgroundColor = [UIColor blackColor];

            label.textColor = [UIColor yellowColor];

            label.textAlignment = UITextAlignmentCenter;

            label.text = [titles objectAtIndex:i];

            [self addSubview:label];

        }

    }

    return self;

}


@end

 
LPTableHeader 클래스는 그리드뷰의 타이틀을 표시하기 위한 UITableView를 위한 커스텀 헤더이다. 헤더에 그림자 효과를 주기 위해 CALayer의 프라퍼티를 사용했다. 그러므로 프로젝트에 QuartzCore 프레임워크를 추가하고 임포트를 해야 한다. 나머지 부분은 타이틀의 갯수만큼 UILable을 추가하는 코드인데, 예에서는 라벨의 넓이를 100로 설정했다.

다음은 커스텀 셀을 위해 UITableViewCell를 상속하는  LPTableViewCell 클래스를 추가하고 다음과 같이 수정한다.

[ LPTableViewCell.h]

#import <UIKit/UIKit.h>


@interface LPTableViewCell : UITableViewCell


@property (nonatomic, strong) IBOutlet UILabel *nameLabel;

@property (nonatomic, strong) IBOutlet UILabel *priceLabel;

@property (nonatomic, strong) IBOutlet UILabel *discountRateLabel;

@property (nonatomic, strong) IBOutlet UILabel *inventoryLabel;

@property (nonatomic, strong) IBOutlet UILabel *createDateLabel;


@end

 
 [ LPTableViewCell.m]

#import "LPTableViewCell.h"


@implementation LPTableViewCell


@synthesize nameLabel;

@synthesize priceLabel;

@synthesize discountRateLabel;

@synthesize inventoryLabel;

@synthesize createDateLabel;


- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier

{

    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];

    if (self) {

        // Initialization code

    }

    return self;

}


- (void)setSelected:(BOOL)selected animated:(BOOL)animated

{

    [super setSelected:selected animated:animated];


    // Configure the view for the selected state

}


@end

 
이 번에는 수정한 부분이 특별히 없다. 실제 좌우로 스크롤되는 테이블의 다중셀을 위한 Property와 Synthesize를 추가했을 뿐이다. 예에서는 간단히 작업하기 위해 커스텀 셀을 XIB를 사용하여 작성하는 방법을 선택하였다. 이에 대해서는 이미 알고 있다는 전제하에 설명은 생략한다.
이외에도 XIB를 사용하지 않고 UITableViewCell 만을 사용하거나 UITableViewDataSource에 프로그램적으로 적용하는 방법을 선택할 수 있다.

이 단계의 마지막은 LPTableViewCell.xib를 추가하고 다음 그림과 같이 아웃렛을 연결하고 커스텀 클래스를 설정하는는 것이다.


[혹, 아직 모르는 분들을 위해...]
Custom UITableViewCell Using Interface Builder 
Easy custom UITableView drawing

또는 다음과 같이 UINib를 사용하는 방법도 있다.
 UINib 클래스의 활용: 커스텀 UITableViewCell
 
자 이제 이 튜토리얼에서 제일 중요한 LPGridView를 프로젝트에 추가하자. 물론 UIView를 상속해야 한다. 
 
[LPGridView.h]

#import <UIKit/UIKit.h>


@interface LPGridView : UIView


@property (nonatomic, strong) UIScrollView *background;

@property (nonatomic, strong) UITableView *indexTable;

@property (nonatomic, strong) UITableView *gridTable;

@property (nonatomic, assign) CGFloat indexTableWidth;

@property (nonatomic, assign) CGFloat gridTableWidth;

@property (nonatomic, strong) id delegateObj;


- (void)relaodGridView;


@end

 
[LPGridView.m]

#import "LPGridView.h"


@implementation LPGridView


@synthesize background;

@synthesize indexTable;

@synthesize gridTable;

@synthesize indexTableWidth;

@synthesize gridTableWidth;

@synthesize delegateObj;


- (id)initWithFrame:(CGRect)frame

{

    self = [super initWithFrame:frame];

    if (self

    {

     

    }

    return self;

}


- (void)drawRect:(CGRect)rect

{

    CGFloat x = rect.origin.x;

    CGFloat y = rect.origin.y;

    CGFloat width = rect.size.width;

    CGFloat height = rect.size.height;

    

    // 인덱스 테이블.

    self.indexTable = [[UITableView alloc] initWithFrame:CGRectMake(x, y, self.indexTableWidth, height)];

    self.indexTable.showsVerticalScrollIndicator = NO;

    self.indexTable.dataSource = self.delegateObj;

    self.indexTable.delegate = self.delegateObj;

    [self addSubview:self.indexTable];

    

    // 그리드뷰 백그라운드.

    self.background = [[UIScrollView alloc] initWithFrame:CGRectMake(self.indexTableWidth, 0, width - self.indexTableWidth, height)];

    self.background.contentSize = CGSizeMake(self.gridTableWidth, height);

    self.background.delaysContentTouches = NO;

    self.background.canCancelContentTouches = NO;

    self.background.bounces = NO;

    self.background.delegate = self.delegateObj;

    [self addSubview:self.background];

    

    // 그리드뷰.

    self.gridTable = [[UITableView alloc] initWithFrame:CGRectMake(0.0, y, self.gridTableWidth, height)];

    self.gridTable.dataSource = self.delegateObj;

    self.gridTable.delegate = self.delegateObj;

    [self.background addSubview:self.gridTable];

}


// 데이터 리로드.

- (void)relaodGridView

{

    [self.indexTable reloadData];

    [self.gridTable reloadData];

}


@end

 
LPGridView에는 모두 여섯 개의 프라퍼티를 선언했다. background의 타입은 UIScrollView이고 좌우로 스크롤되는  gridTable를 포함한다. UITableView 타입의 indexTable은 좌우 스크롤 시 좌측에 고정되는 테이블이다.(맨 위 그림에서, 등록일자). 그리고 indexTableWidth와 gridTableWidth는 각각 indexTable과 gridTable의 넓이를 설정하기 위한 프라퍼티이다. 마지막 프라퍼티는는 id 타입의 delegateObj 프라퍼이다. 이는 데이터 출력을 위해 UITableView를 사용했으므로, UITableViewDataSource와 UITableViewDelegate를 설정하기 위한 것과 더불어 백그라운드로 사용하는 UIScrollView의 델리게이트를 설정하기 위한 용도이다. 
마지막으로 각 테이블의 데이터를 동시(???)에 리로드하기 위해 reloadGridViw 메서드를 추가했다.

이제 거의 끝나간다. 여기서 잠깐 LPGridView의 샘플 데이터를 위한 SampleData.plist를 프로젝트에 추가하고 다음 그림과 같이 샘플 데이터를 입력하자.


마지막 단계는 LPViewController 클래스에 LPGridView를 적용하는 일이다. 프로젝트 생성 시 템플릿으로 생성된 클래스를 다음과 같이 수정하자.

[LPViewController.h]

#import <UIKit/UIKit.h>


@class LPGridView;


@interface LPViewController : UIViewController <UIScrollViewDelegate, UITableViewDataSource, UITableViewDelegate>


@property (nonatomic, strong) LPGridView *gridView;

@property (nonatomic, strong) NSMutableArray *dataSet;


@end


[LPViewController.m]

#import "LPViewController.h"

#import "LPGridView.h"

#import "LPTableHeader.h"

#import "LPTableViewCell.h"


#define kIndexTableWidth 100.0

#define kGridTableWidth 500.0


@implementation LPViewController


@synthesize gridView;

@synthesize dataSet;


- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil

{

    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];

    if (self

    {

    

    }

    return self;

}


- (void)didReceiveMemoryWarning

{

    [super didReceiveMemoryWarning];

    // Release any cached data, images, etc that aren't in use.

}


#pragma mark - View lifecycle


- (void)viewDidLoad

{

    [super viewDidLoad];

    

    // 그리드뷰 추가.

    self.gridView = [[LPGridView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 460)];

    self.gridView.delegateObj = self;

    self.gridView.indexTableWidth = kIndexTableWidth;

    self.gridView.gridTableWidth = kGridTableWidth;

    [self.view addSubview:self.gridView];

    [self.gridView setNeedsDisplay];

    

    // 데이터 로드.

    NSString *path = [[NSBundle mainBundle] pathForResource:@"SampleData" ofType:@"plist"];

    self.dataSet = [[NSMutableArray alloc] initWithContentsOfFile:path];

    [self.gridView relaodGridView];

}


- (void)viewDidUnload

{

    [super viewDidUnload];

    // Release any retained subviews of the main view.

    // e.g. self.myOutlet = nil;

}


- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation

{

    // Return YES for supported orientations

    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);

}


#pragma mark - 스크롤뷰 델리게이트


- (void)scrollViewDidScroll:(UIScrollView *)scrollView

{    

    // 상하 스크롤할 때는 개의 테이블이 시에 움직여야 하기 때문에...

    if ([scrollView isEqual:self.gridView.indexTable]) 

    {

        self.gridView.gridTable.contentOffset = self.gridView.indexTable.contentOffset;

    }

    else if ([scrollView isEqual:self.gridView.gridTable])

    {

        self.gridView.indexTable.contentOffset = self.gridView.gridTable.contentOffset;

    }

}


#pragma mark - Table view data source


- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView

{

    return 1;

}


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

{

    return [self.dataSet count];

}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

{

    if (tableView == self.gridView.indexTable

    {

        static NSString *CellIdentifier = @"Cell";

        

        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

        if (cell == nil

        {

            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];

            cell.selectionStyle = UITableViewCellSelectionStyleGray;

        }

        

        cell.contentView.backgroundColor = (indexPath.row & 1) ? [UIColor darkGrayColor]: [UIColor whiteColor];

        cell.textLabel.backgroundColor = [UIColor clearColor];

        cell.textLabel.font = [UIFont systemFontOfSize:15];

        cell.textLabel.text = [[self.dataSet objectAtIndex:indexPath.row] objectForKey:@"regDate"];

        

        return cell;

    }

    else if (tableView == self.gridView.gridTable)

    {

        static NSString *CellIdentifier = @"Cell";

        

        LPTableViewCell *cell = (LPTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];

        if (cell == nil

        {

            NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"LPTableViewCell" owner:self options:nil];

            

            for (id currentObject in topLevelObjects)

            {

                if ([currentObject isKindOfClass:[UITableViewCell class]])

                {

                    cell =  (LPTableViewCell *)currentObject;

                    break;

                }

            }

            

            cell.selectionStyle = UITableViewCellSelectionStyleGray;

        }

        

        // Configure the cell...

        cell.contentView.backgroundColor = (indexPath.row & 1) ? [UIColor darkGrayColor]: [UIColor whiteColor];

        cell.nameLabel.text = [[self.dataSet objectAtIndex:indexPath.row] objectForKey:@"name"];

        cell.priceLabel.text = [[self.dataSet objectAtIndex:indexPath.row] objectForKey:@"price"];

        cell.discountRateLabel.text = [[self.dataSet objectAtIndex:indexPath.row] objectForKey:@"discountRate"];

        cell.inventoryLabel.text = [[self.dataSet objectAtIndex:indexPath.row] objectForKey:@"inventory"];

        cell.createDateLabel.text = [[self.dataSet objectAtIndex:indexPath.row] objectForKey:@"createDate"];

        

        return cell;

    }

    

    return nil;

}


#pragma mark - Table view delegate


- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section

{

    return 25;

}


- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section

{

    if (tableView == self.gridView.indexTable

    {

        LPTableHeader *header = [[LPTableHeader alloc] initWithFrame:CGRectMake(0.0, 0.0, kIndexTableWidth, 25) andTitles:[NSArray arrayWithObjects:@"등록일자", nil]];

        return header;

    }

    else if (tableView == self.gridView.gridTable

    {

        LPTableHeader *header = [[LPTableHeader alloc] initWithFrame:CGRectMake(0.0, 0.0, kGridTableWidth, 25) andTitles:[NSArray arrayWithObjects:@"이름", @"가격", @"한인율", @"재고", @"생산일자"nil]];

        return header;

    }

    

    return nil;

}


- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

{

    if (tableView == self.gridView.indexTable

    {

        [self.gridView.gridTable selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];

    }

    else if (tableView == self.gridView.gridTable

    {

        [self.gridView.indexTable selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];

    }

}


@end


 LPViewController는 일반적인 UIScrollView와 UITableView를 위한 코드와 별반 차이가 없으므로, 다음두 가지만 설명하고 주의하면 될 것이다.
viewDidLoad 메서드서 그리드뷰를 생성하여 추가하고 샘플 데이터를 로드했다. 그리고 스크롤뷰의 델리게이트 메서드인  - (void)scrollViewDidScroll:(UIScrollView *)scrollView에서 indexTable과 gridTable의 contentOffset 값을 항상 일치하게 하는 코드를 추가했다. 이는 UITableView가 UIScrollView를 상속했기 때문에 델이게이트 메서드를 사용할 수 있다. 또한 위에서 본 요구사항의 그림에서 상하 스크롤할 때는 두 개의 테이블이 동 시에 움직여야 하기 때문이다.

자 이제 프로젝트를 빌드하고 실행해 보자. 요구사항의 그림을 직접 확인할 수 있을 것이다.

샘플 프로젝트: SimpleGridView 


 
저작자 표시

'Tutorial' 카테고리의 다른 글

초간단 그리드뷰  (0) 2011/12/28
초간단 델리게이션과 서브클래싱 예제  (0) 2011/11/17
뉴스 티커 만들기  (0) 2011/11/09
Mac에 GitLab 설치하기  (0) 2011/10/20
UINib 클래스의 활용: 커스텀 UITableViewCell  (0) 2011/09/27
간단한 바 차트 그리기  (0) 2011/08/09
« PREV : 1 : 2 : 3 : 4 : 5 : ... 108 : NEXT »