2012년 12월 13일 목요일

[OSX] 숨긴 폴더 및 파일 표시

터미널을 통해 다음 명령어를 실행한다.
defaults write com.apple.Finder AppleShowAllFiles YES

Finder 강제 종료 후 재실행이 필요하다.

[iOS] Volume Purchasing for Business Program


 어제, 미국에 한해 iOS 앱을 카피 단위로 판매할 수 있는 Volume Purchasing for Business Program(VPP)이 새로 발표됐다. 볼륨 앱 스토어를 통해 2~1000 Copy 단위(Volume)로 앱을 판매/구매할 수 있으며, 구매한 앱은 리딤 코드를 사용하여 앱스토어를 거치지 않고 자체적으로 배포가 가능하다. 아직은 미국 기업에 한정되어 있지만, 곧 한국에도 적용이 될 것이라 생각한다. 개인적인 차원에서도, 신규 사업분야 진출의 일환으로 모바일 사업을 준비 중인 팀 차원에서도 여러모로 큰 도움이 될 것 같다.

* 2013년 1월 현재, 미국 뿐 아니라 호주, 캐나다, 프랑스 등 10개 국가로 확대되었습니다.

[iOS] 루트 뷰 컨트롤러 표시하기

popToRootViewControllerAnimated: 메시지를 이용하면 루트 컨트롤러를 바로 표시할 수 있다.
[self.navigationController popToRootViewControllerAnimated:YES];


미리 알았다면 이런 짓은 안했을텐데.
for (int i = 0; i < [self.navigationController.viewControllers count]; i++) {
     [self.navigationController popViewControllerAnimated:NO];
}

[ASP.NET MVC] IValidatableObject 개체 테스트하기

IValidatableObject 인터페이스를 이용하여 유효성 검사를 수행하는 객체는 Validator 클래스를 통해 테스트할 수 있다. 다음은 테스트 주도 ASP.NET MVC 프로그래밍 책의 예제를 개선한 코드들이다. 참고로 책의 예제에서는 유효성 검사를 위해 커스텀 인터페이스를 사용하고 있다.

public class Todo : IValidatableObject
{
    //[Required]
    //[StringLength(25)]
    public string Title { get; set; }

    public IEnumerable<ValidationResult> Validate(
        ValidationContext validationContext)
    {
        if (string.IsNullOrEmpty(Title))
            yield return new ValidationResult(
                "제목이 입력되지 않았습니다.", 
                new[] { "Title" });

        if (Title != null && Title.Length > 25)
            yield return new ValidationResult(
                "제목이 25자를 초과할 수 없습니다.", 
                new[] { "Title" });
    }
}


제목 길이에 따른 각 개체들의 유효성 검사는 다음과 같이 테스트 한다.
[TestFixture]
public class TodoTest
{
    [Test]
    public void Title_Length_Should_Be_To_Maximum_Of_25_Characters()
    {
        // Arrange
        Todo longTodo = new Todo { Title = "123456789ABCDEF123456789ABCDEF" };
        Todo twentyFiveCharacterTodo = new Todo { Title = "123456789ABCDEF1234567" };
        Todo shortTodo = new Todo { Title = "123456789" };

        // Assert
        Assert.IsFalse(IsValid(longTodo));
        Assert.IsTrue(IsValid(twentyFiveCharacterTodo));
        Assert.IsTrue(IsValid(shortTodo));
    }

    private bool IsValid(IValidatableObject toValidate)
    {
        return Validator.TryValidateObject(
            toValidate, 
            new ValidationContext(toValidate, null, null), 
            null, 
            true);
    }
}


에러 메시지를 확인하고 싶다면 ValidationResult 리스트를 사용하면 된다.
[Test]
public void Title_Length_Should_Be_To_Maximum_Of_25_Characters()
{
    // Arrange
    Todo longTodo = new Todo { Title = "123456789ABCDEF123456789ABCDEF" };

    var result = new List<ValidationResult>();

    Validator.TryValidateObject(
        longTodo,
        new ValidationContext(longTodo, null, null),
        result,
        true);
        
    // Assert
    Assert.AreEqual(1, result.Count);
    Assert.AreEqual("제목은 25자를 초과할 수 없습니다.", result[0].ErrorMessage);
}

2012년 12월 12일 수요일

[ASP.NET MVC] ASP.NET MVC4 테스트 프로젝트에서 MVCContrib 사용 시 예외 발생 해결

ASP.NET MVC4  + MVCContrib 프레임워크를 사용하는 프로젝트들을 테스트하면 MVCContrib의 몇 몇 확장 메서드들이(ShouldMapTo<T>(), AssertActionRedirect<T>()와 같은) 이상한 예외들을 발생시킨다. 동일한 두 개의 타입을 틀린 타입으로 취급한다.

MvcContrib.TestHelper.ActionResultAssertionException : Expected result to be of type RedirectToRouteResult. It is actually of type RedirectToRouteResult.

MVCContrib3에서 참조되는 System.Web.Mvc.dll의 버전(1.0 ~ 3.0)이 테스트 프로젝트에서 참조된 버전(4.0)과 다르기 때문에 발생하는 문제다. App.config를 통해 1.0 ~ 3.0 버전을 강제로 4.0으로 바인딩하여 해결한다. 테스트 클래스 프로젝트에 App.config를 추가하고 다음과 같이 입력해준다.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <runtime>
        <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
            <dependentAssembly>
                <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
                <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="4.0.0.0" />
            </dependentAssembly>
        </assemblyBinding>
    </runtime>    
</configuration>

2012년 11월 22일 목요일

[ASP.NET MVC] 인증 필터 적용 여부 테스트

컨트롤러에 적용된 특성(Attribute)들은 리플렉션을 이용해 테스트하면 된다.
Assert.IsTrue(typeof(HomeController)
    .GetCustomAttributes(false)
    .Any(o => o.GetType() == typeof(AuthorizeAttribute)));

2012년 11월 21일 수요일

[ASP.NET MVC] 몇 가지 테스트 케이스들.

ViewResult

액션 메서드의 실행 결과는 ViewResult 클래스를 통해 테스트한다.
[Test]
public void Should_Load_Index_View()
{
    var viewResult = new HomeController().Index() as ViewResult;

    // 뷰 이름 확인
    Assert.AreEqual("Index", viewResult.ViewName);
}


RedirectToRouteResult

POST 액션 메서드의 테스트는 모델 테스트를 포함한다. CRUD 작업 후 처리 방식에 따라 테스트 항목을 결정.
[Test]
public void Should_Delete_Item()
{
    var deleteItem = Items.FindBy("id");
    var redirectToRouteResult = 
        (RedirectToRouteResult)new SampleController().Delete(deleteItem);

    // Item이 제대로 삭제되었는지 확인
    Assert.IsFalse(Items.All.Contains(deleteItem));

    // 삭제 후 Index 뷰가 표시됐는지 확인
    Assert.AreEqual("Index", redirectToRouteResult.RouteValues["action"]);
}


FormCollection

모델 바인딩을 사용할 수 없는 POST 액션 메서드는 FormCollection 클래스를 이용해 테스트.
[Test]
public void Should_Create_Item()
{
    var item = new Item { Id = "1", Name = "Test Item" };

    var formValues = new FormCollection();
    formValues.Add("Id", item.Id);
    formValues.Add("Name", item.Name);

    var controller = new SampleController();
  
    var result = controller.Create(formValues) as RedirectToRouteResult;

    Assert.IsTrue(Items.All.Contains(item));
    Assert.AreEqual("Index", redirectToRouteResult.RouteValues["action"]);
}


.

2012년 11월 20일 화요일

[iOS] UIWebView에서 자바스크립트 실행하기

stringByEvaluatingJavaScriptFromString 메시지를 이용하면 웹페이지의 자바스크립트 코드를 실행하고 반환값을 받을 수 있다. (스크립트)실행 시간이 10초를 넘으면 자동으로 실행 취소가 되며 nil을 반환하니 주의할 것.
// getLayoutStyle 스크립트 메서드를 실행한다.
NSString* returnValue = 
    [self.webView stringByEvaluatingJavaScriptFromString:@"getLayoutStyle"];

2012년 11월 15일 목요일

[iOS] UITableView의 바운스 및 스크롤 오류

UITableView와 UISearchBar를 동시에 사용하면 UITableView의 바운스와 스크롤이 안되는 경우가 있는데, 다음 코드를 viewDidLoad 메서드에 추가하면 해결할 수 있다.

self.tableView.bounces = YES;

참조

[jQuery] XML 파싱


jQuery.parseXML() 메서드를 이용해 xml 문자열을 파싱한다.
        $xml = $($.parseXML(xml))
        $item = $xml.find("item:first");

        alert($(item).text());

[ASP.NET MVC] 브라우저 캐시 회피하기

 현재 개발 중인 모바일 버전 전자결재 서비스의 결재 시나리오다. 사용자가 [문서목록 페이지]에서 문서를 터치하면 [결재 페이지]가 표시된다. 결재 버튼을 터치하면 문서가 결재됐다는 알림과 함께 [문서목록 페이지]를 이동한다. 여기서 웹 브라우저의 'Back' 버튼이 문제가 된다. 'Back' 버튼을 누르면 이미 결재가 완료된 문서이기 때문에 한 단계 더 뒤로 가던가(history.back x 2), 결재가 완료된 문서에 접근했음을 알리는 알림이 필요하다. 우리는 간단히 알림을 제공하도록 [결재 페이지]의 최상단에 다음 스크립트를 입력했다.

     <% if (Model.IsApproved) { %>
        <script type="text/javascript">
                alert("이미 결재가 완료된 문서입니다.");
                document.location.href = "/Approval/List";
        </script>
    <%} %>

하지만 결재 완료 후 'Back' 버튼을 누르면 스크립트는 무시되고 다시 [결재 페이지]가 표시된다. 이미 결재가 완료된 문서이기 때문에 결재, 반려 등의 UI가 표시되는건 문제가 된다. 스크립트가 무시되는 이유는 캐시 때문이다. 캐시에 저장된 웹 페이지가 스크립팅되지 않고 바로 표시되기 때문이다.

캐시 페이지가 아닌 Brand New 페이지를 표시하기 위해 [결재 페이지]의 액션 메서드에 다음 코드를 추가한다.

    // 캐시를 사용하지 않는다.
    Response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache);
    Response.Cache.SetNoStore();

But. 
크롬에서는 정상적으로 작동된다. 문제는 사파리였다. 실제 아이폰 애플리케이션에서는 위의 코드가 전혀 소용이 없다. 'Back' 버튼을 터치하면 캐시된 [결재 페이지]로 이동한다. 반나절을 구글과 StackOverflow를 해집고 돌아다니던 중 이유와 해결 방법을 찾았다. 최신 웹 브라우저들은(크롬은 되던데?) 일반적인 HTTP 캐시와는 차별되는Back/Foward를 위한 별도의 캐시를 제공한다. 이 때문에 위의 코드들이 사파리에서는 작동되지 않았던거다.

해결 방법은 '믿든지 말든지 (StackOverflow의 어느 개발자의 답글이었다. believe it or not.)' body 태그의 unload 이벤트에 빈 핸들러만 등록해주면 된다. 뭐 이런 경우가 있나 싶지만, 자~알 작동한다.이유는 참고 자료의 두 번째 링크를 통해 알 수 있다.

    <body onunload="">


참고

'Microsoft.JET.OLEDB.4.0' 공급자는 로컬 컴퓨터에 등록할 수 없습니다.

System.Data.OleDb.OleDbDataAdapter 클래스를 생성하는데 다음과 같은 예외가 발생했다.

  •  'Microsoft.Jet.OLEDB.4.0' 공급자는 로컬 컴퓨터에 등록할 수 없습니다.
  •  'Microsoft.ACE.OLEDB.12.0' 공급자는 로컬 컴퓨터에 등록할 수 없습니다. 


원인 

구글링 결과 보통 두 가지 이유 때문에 발생하는 예외라 한다. x86 서버에서는 잘 돌아가던 웹 서비스가 x64 서버에서만 예외가 발생하니 아마도 두 번째 이유가 원인인 듯 하다.

  1. 서버에 Office System Driver가 설치되지 않았을 경우 
  2. x64 서버에서 웹 서비스를 호스팅할 경우 


해결 

첫 번째 경우는 Office System Driver를 설치하면 간단하게 해결된다.

 [2007 Office system 드라이버 : 데이터 연결 구성 요소 다운로드] 

두 번째 경우는 등록된 OLE DB Provider가 x64 플랫폼을 지원하지 않기 때문이다. IIS 설정을 변경하거나 프로젝트 빌드 플랫폼 변경을 통해 x86 모듈에 대한 호환성을 추가해준다.

IIS 설정 변경 (IIS7) 
IIS관리자에서 응용 프로그램 풀을 선택. 우 클릭하여 '고급 설정'을 선택한다. '32비트 응용 프로그램 사용'을 'True'로 변경한다.



빌드 플랫폼 변경 (VS2010) 
프로젝트 이름을 클릭하고 우 클릭, '속성'을 선택하여 '빌드'탭의 '플랫폼 대상'을 'x86'으로 변경한다.



2012년 11월 12일 월요일

[MSSQL] DATEPART 함수

DATEPART(datepart, date) 함수를 통해 날짜 별 통계 쿼리 작성이 가능하다. datapart 인수를 통해 년, 월, 주, 일별로 날짜를 뽑아내서 GROUP BY를 수행한다. 자주 잊어버리는 함수라 기록 차원에서 포스팅.


-- 월 별 사용량 통계
SELECT 
    datepart(mm, CREATE_DATE) AS '월', 
    (sum(FILESIZE) / 1024) AS 'MB' 
FROM dbo.EP_FILEINFOT 
WHERE 
    DIRECTORY = 'CONTENTS.GROUP.APPRMAIL_ALL' AND 
    ISDELETED = 'N' AND 
    CREATE_DATE > '2011-01-01'
GROUP BY datepart(mm, CREATE_DATE);

참고자료


  • http://msdn.microsoft.com/en-us/library/ms174420.aspx

2012년 11월 7일 수요일

[ASP.NET MVC] IControllerActivator

ASP.NET MVC 1, 2에서는 사용자 요청을 처리할 컨트롤러를 생성하는 역할을 IControllerFactory 인터페이스가 책임졌다. IContollerFactory 인터페이스는 다음과 같은 역할을 한다.
  1. 요청을 처리할 컨트롤러를 결정
  2. 컨트롤러 생성

이는 클래스 당 한 가지 책임(기능)을 가진다는 단일책임원칙(SRP)에 위배된다. ASP.NET MVC 3 에서는 리팩토링을 통해 IControllerFactory의 역할을 분리하여 IControllerActivator라는 새로운 인터페이스를 추가했다.
  • IControllerFactory : 요청을 처리항 컨트롤러를 결정
  • IControllerActivator : 컨트롤러를 생성

요청을 처리할 컨트롤러가 결정되면 컨트롤러 팩토리 내부에서 IControlerActivator의 Crate 메서드가 호출된다. 사용자 정의 IControllerActivator를 사용하기 위해서는 IDependencyResolver를 통해 새로 구현된 IControllerActivator를 반환하도록 한다.

결론은,
IoC 컨테이너를 사용하려면 커스텀 컨트롤러 팩토리 클래스를 구현할게 아니라 커스텀 DependencyResolver와 ControllerActivator 클래스를 구현해야 한다는 것.


참고

[ASP.NET MVC] 라우팅 데이터를 이용한 URL 생성

간단한 URL도 하드코딩하지 않고 동적으로 생성하는 버릇을 들일 필요가 있다. 귀찮다고 하드코딩 했다가는 유지보수할 때 고생한다. -_-;;

<% Url.RouteUrl(new { controller = "Home", action = "Index" }); %>

2012년 10월 29일 월요일

EWS Managed API를 이용한 연락처 정보 가져오기

MS Exchange Web Service Managed API

MS Exchange Web Service Managed API를 이용하면 EWS를 쉽게 이용할 수 있다. 아는게 없어 SOAP 정보를 하나 하나 분석해가며 사용했던 기억이 새록 새록..

여기서 API를 다운로드하고 실행하면 Program Files\Microsoft\Exchange\Web Services 폴더에 1.2와 2.0 버전의 API가 설치된다. 2.0 폴더의 Microsoft.Exchange.WebServices.dll 을 프로젝트에 참조하고 시작한다.

// 기본 버전은 Exchange2010_SP2 이다. 
ExchangeService service = new ExchangeService(
    ExchangeVersion.Exchange2007_SP1)
{
    Credentials = new WebCredentials("id", "pwd", "domain"),
    Url = new Uri("http://contoso.com/EWS/exchange.asmx")
};

// 연락처 폴더를 바인딩.
var contacts = Folder.Bind(service, 
    new FolderId(WellKnownFolderName.Contacts));

FindItemsResults findResults = service.FindItems(
    WellKnownFolderName.Contacts, 
    new ItemView(contacts.TotalCount));

foreach (var each in findResults)
{
    if (each is Contact)
    {
        Contact contact = each as Contact;
        // contact 변수를 이용해 연락처 정보를 가져온다.
    }
}


위 코드에서는 연락처 정보를 가져왔지만 (WellKnownFolderName.Contacts), 어떤 폴더를 바인딩 하냐에 따라 메일, 캘린더, 작업 등 다양한 아이템들을 가져올 수 있다. 참고로 WellKnownFolderName 구조체의 정의는 다음과 같다.

namespace Microsoft.Exchange.WebServices.Data
{
    public enum WellKnownFolderName
    {
        Calendar = 0,
        Contacts = 1,
        DeletedItems = 2,
        Drafts = 3,
        Inbox = 4,
        Journal = 5,
        Notes = 6,
        Outbox = 7,
        SentItems = 8,
        Tasks = 9,
        MsgFolderRoot = 10,
        [RequiredServerVersion(ExchangeVersion.Exchange2007_SP1)]
        PublicFoldersRoot = 11,
        Root = 12,
        JunkEmail = 13,
        SearchFolders = 14,
        VoiceMail = 15,
        [RequiredServerVersion(ExchangeVersion.Exchange2010_SP1)]
        RecoverableItemsRoot = 16,
        [RequiredServerVersion(ExchangeVersion.Exchange2010_SP1)]
        RecoverableItemsDeletions = 17,
        [RequiredServerVersion(ExchangeVersion.Exchange2010_SP1)]
        RecoverableItemsVersions = 18,
        [RequiredServerVersion(ExchangeVersion.Exchange2010_SP1)]
        RecoverableItemsPurges = 19,
        [RequiredServerVersion(ExchangeVersion.Exchange2010_SP1)]
        ArchiveRoot = 20,
        [RequiredServerVersion(ExchangeVersion.Exchange2010_SP1)]
        ArchiveMsgFolderRoot = 21,
        [RequiredServerVersion(ExchangeVersion.Exchange2010_SP1)]
        ArchiveDeletedItems = 22,
        [RequiredServerVersion(ExchangeVersion.Exchange2010_SP1)]
        ArchiveRecoverableItemsRoot = 23,
        [RequiredServerVersion(ExchangeVersion.Exchange2010_SP1)]
        ArchiveRecoverableItemsDeletions = 24,
        [RequiredServerVersion(ExchangeVersion.Exchange2010_SP1)]
        ArchiveRecoverableItemsVersions = 25,
        [RequiredServerVersion(ExchangeVersion.Exchange2010_SP1)]
        ArchiveRecoverableItemsPurges = 26,
    }
}


참고

2012년 10월 24일 수요일

[iOS] 통신사 정보 및 국가 코드 가져오기

CoreTelephony 프레임워크의 CTTelephonyNetworkInfo, CTCarrier 클래스를 이용해 기기의 통신사 정보를 가져올 수 있다. 프로젝트에 CoreTelephony.framework 를 등록하고 다음 코드를 추가한다.

#import <CoreTelephony/CTCarrier.h>
#import <CoreTelephony/CTTelephonyNetworkInfo.h>

CTTelephonyNetworkInfo *networkInfo = [[CTTelephonyNetworkInfo alloc] init];
CTCarrier *carrier = [networkInfo subscriberCellularProvider];
    
NSLog(@"Carrier Name : %@", [carrier carrierName]);
NSLog(@"MNC : %@", [carrier mobileNetworkCode]);
NSLog(@"MCC : %@", [carrier mobileCountryCode]);
    
[networkInfo release];

통신사 식별자로 사용되는 MNC (Mobile Network Code)와 국가를 식별하는 MCC (Mobile Country Code)는 각 각 2~3자리의 숫자로 이루어져 있다. 각 코드가 나타내는 국가 및 통신사는 아래 Mobile Network Code 참조에서 확인하면 된다. 참고로 한국의 MCC는 450이고, SKT의 MNC는 11, KT는 08이다.

시뮬레이터에서 실행하면 모두 nil로 표시된다. 디바이스에서 실행하면 다음과 같은 결과를 확인할 수 있다.
Carrier Name : KT
MNC : 08
MCC : 450


참고



2012년 10월 12일 금요일

맥북프로에서 CD, DVD, USB로 부팅하기

부트캠프로 윈도8을 설치하고서 파티션 가지고 장난치다기 하드 전체를 날려 먹었다.
눈물을 머금고 OS X부터 재설치하려 하는데 DVD부팅이 안된다. PC 같으면 Del, F2, F11 중 하나일텐데?..

구글링 해보니 맥에도 비슷한 부팅 관련 기능들이 있었다.
부팅 중에 "C" 키를 누르면 CD, DVD 또는 USB로 부팅이 가능하다.

이 외에 많은 부팅 옵션들이 있으니, 자세한 내용은 다음 링크에서 확인할 수 있다.

Intel 기반 Mac의 시동 키 조합

2012년 10월 9일 화요일

Visual Studio 2012 설치 중 멈춤 현상

부트캠프를 이용해 Windows 8 Pro를 설치한 맥북 프로에 Visual Studio 2012를 설치하면 맥북이 멈춰버리는 현상이 발생한다. 몇 번을 설치해봐도 동일한 증상이 발생한다. 구글링해보니 동일한 증상으로 고생하는 사람들이 있었다.

이유는 잘 모르겠지만, 해결 방법만 대충 정리해둔다. 참고로 맥북 프로는 2010년 17인치 모델이다.

1. 그래픽 카드 드라이버를 최신으로 업데이트 한다.
2. 명령 프롬프트를 관리자 권한으로 실행하여 다음 명령어를 입력한다. (어떤 역할의 명령어인지는 모른다..-_-)

bcdedit /set disabledynamictick yes


이 후 설치는 정상적으로 잘 진행된다. 참고하시길.


2012년 7월 12일 목요일

[iOS] Splash Screen 추가

Splash Screen

iOS 앱이 실행될 때 잠시동안 표시되는 이미지를 Splash Screen이라 한다. iOS 앱에서의 Splash Screen을 구현하려면 뷰를 따로 개발할 필요없이 정해진 이름의 PNG 이미지만 추가해주면 된다. 파일명이 "Default.png", "Default@2x.png"인, 320*480, 640*960 사이즈를 가진 이미지 파일을 프로젝트에 추가하면 자동으로 Splash Screen로 표시된다.

주의할 점은 파일 이름이 대/소문자를 구별한다는 사실이다. "Default.png" 만 제대로 표시된다. 파일 이름이 "default.png" (시작이 소문자라면)일 경우엔 iPhone Simulator에서는 정상적으로 표시되지만 실제 디바이스에서는 표시되지 않는다. (사소한 문제지만 이걸로 몇 시간을 날렸다.)

추가

iPhone 5를 이용한 Splash Screen의 파일이름은 "Default-586h@2x.png" 다.

2012년 7월 10일 화요일

[iOS] InstalledApplicationList Command 요청 결과

InstalledApplicationList 커맨드를 이용하여 기기에 설치된 앱들의 정보를 알 수 있다. 기본 앱은 제외된다.iOS 디바이스의 응답은 다음과 같다.

Device Response

<plist version="1.0">
    <dict>
        <key>CommandUUID</key>
        <string>1a3cbacc-38f0-4dcd-a05f-f5ec7d1b8107</string>
        <key>InstalledApplicationList</key>
        <array>
            <dict>
                <key>BundleSize</key>
                <integer>27353088</integer>
                <key>DynamicSize</key>
                <integer>16384</integer>
                <key>Identifier</key>
                <string>com.google.chrome.ios</string>
                <key>Name</key>
                <string>Chrome</string>
                <key>ShortVersion</key>
                <string>19.1084.60</string>
                <key>Version</key>
                <string>19.0.1084.60</string>
            </dict>
        </array>
        <key>Status</key>
        <string>Acknowledged</string>
        <key>UDID</key>
        <string>[[UDID]]</string>
    </dict>
</plist>

2012년 7월 4일 수요일

[iOS] 매크로를 이용한 펑션 이름, 코드라인, 파일네임 출력


// 파일 명, 코드 라인, 메서드 명
NSLog(@"File Name : %s, Code Line : %d, Method Name : %s", 
    __FILE__, 
    __LINE__, 
    __FUNCTION__)

2012년 6월 22일 금요일

[iOS] UIDevice 클래스를 통한 디바이스 정보 가져오기

UIDevice

UIDevice 클래스는 기기 정보와 관련된 여러 프로퍼티들을 가지고 있다. 사용 빈도에 비해 유난히 기억이 안나 늘 찾아보게 되는 구문이다. 기억 차원에서 포스팅.


UIDevice *device = [UIDevice currentDevice];

// e.g. "My iPhone"
NSLog(@"Device Name : %@", device.name);

// e.g. @"iPhone", @"iPod touch"          
NSLog(@"Device Model: %@", device.model);

// e.g. @"4.0"   
NSLog(@"System Name : %@", device.systemVersion);

// e.g. @"iOS"
NSLog(@"System Version : %@", device.name);

// localized version of model
NSLog(@"Localized Model : %@", device.localizedModel);

참조

[iOS] UITextField를 포함하는 Custom UITableViewCell

Tumblr iOS Application

텀블러 앱의 로그인 화면과 같은 입력 폼을 만들기 위해 UITableViewCell을 상속받아 간단한 커스텀 테이블 뷰 셀을 만들었다. 커스텀 테이블 뷰 셀을 만들기 위해선 다음 세 가지만 기억해두면 될 듯 하다.
  1. UITableViewCell 클래스를 상속하여 원하는 뷰를 추가.
  2. initWithStyle:reuseIdentifier: 메시지에서 추가된 뷰를 초기화.
  3. layoutSubviews 메시지를 오버라이드하고 bringSubviewToFront: 메시지를 추가한 뷰를 전면으로 표시한다.
FieldTableViewCell.h
#import 

@interface FieldTableViewCell : UITableViewCell

@property (nonatomic, retain) UITextField *textField;

@end

FieldTableViewCell.m
#import "FieldTableViewCell.h"

@implementation FieldTableViewCell
@synthesize textField;

- (void)dealloc
{
    [self.textField release];
    
    [super dealloc];
}

- (id)initWithStyle:(UITableViewCellStyle)style 
    reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        self.textField = [[UITextField alloc] initWithFrame:CGRectZero];        
        [self.contentView addSubview:self.textField];
    }
    
    return self;
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
    [super setSelected:selected animated:animated];
}

- (void)layoutSubviews
{
    [super layoutSubviews];
    
    [self.contentView bringSubviewToFront:self.textField];
    
    CGSize labelSize = [self.textLabel.text sizeWithFont:self.textLabel.font];
    
    CGRect frameForTextField;
    frameForTextField.size.width = 
        self.contentView.frame.size.width - labelSize.width - 30.0f;
    frameForTextField.size.height = 31.0f;
    frameForTextField.origin.x = labelSize.width + 15.0f;
    frameForTextField.origin.y = 11.0f;
    
    self.textField.frame = frameForTextField;
}

@end