2017/03/28

iOS & Swift : Keyboard가 표시될 때, 사라질 때 이벤트와 그 키보드의 위치를 알아보자

지난 블로그에서는 Objective-C를 기준으로 정리하였는데, 이제는 Swift를 기준으로 정리한다.
(링크 [iOS] Keyboard가 표시될 때, 사라질 때 이벤트와 그 키보드의 위치는?)

일반적인 UIViewController 내에서는 표시할 위치를 이동시켜야, 키보드에 덮히지 않고, 사용자에게 표시할 수 있습니다.

1. 키보드가 화면에 나타나거나, 사라지는 Event는 무엇이고, 어떻게 알아낼까?

키보드가 표시될 때 전달되는 이벤트는...
  • NSNotification.Name.UIKeyboardWillShow : 키보드가 표시되기 전에 호출
  • NSNotification.Name.UIKeyboardDidShow : 키보드가 표시되고 난 후에 호출
  • NSNotification.Name.UIKeyboardWillHide : 키보드를 숨기기 전에 호출
  • NSNotification.Name.UIKeyboardDidHide : 키보드를 숨기고 난 후에 호출
  • NSNotification.Name.UIKeyboardWillChangeFrame : 키보드 모양이 바뀌기 전 (iOS 5 이상)
  • NSNotification.Name.UIKeyboardDidChangeFrame : 키보드 모양이 바뀐 후 (iOS 5 이상)
필요한 이벤트를 viewWillAppear 함수에서 NotificationCenter에 등록합니다.
Source Code
    override func viewWillAppear(_ animated: Bool) {
        //화면이 표시될때, Keyboard표시에 대한 이벤트가 발생하면 호출되는 함수를 등록한다.
        NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow(_:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardDidShow(_:)), name: NSNotification.Name.UIKeyboardDidShow, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide(_:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardDidHide(_:)), name: NSNotification.Name.UIKeyboardDidHide, object: nil)
    }

화면이 사라질 때, 등록한 것을 삭제해야 한다. 물론 삭제하면 더 이상 이벤트가 발생해도 해당 함수를 호출하지 않습니다.

SourceCode
    override func viewWillDisappear(_ animated: Bool) {
        //화면이 사라질 때, keyboard 표시에 대한 이벤트를 받지 않도록, 등록을 삭제한다.
        NotificationCenter.default.removeObserver(self)
    }


이벤트가 발생할 때, 호출하는 함수들을 정의한다. 해당 이름은 뭐든지 관계없으나 인자로 받는 것은 명시를 해줘야 합니다.

SourceCode
    //키보드가 표시 될 때 호출 되는 함수
    func keyboardWillShow(_ notification:NSNotification) {
        print(notification)
        info(name: "Keyboard Will beShown", with: notification)
        somethingDo(with: notification)
    }
    func keyboardDidShow(_ notification:NSNotification) {
        info(name: "Keyboard Was  Shown", with: notification)
    }
    
    //키보드가 사라질 때, 호출 되는 함수
    func keyboardWillHide(_ notification:NSNotification) {
        info(name: "Keyboard Will beHidden", with: notification)
        somethingDo(with: notification)
    }
    func keyboardDidHide(_ notification:NSNotification) {
        info(name: "Keyboard Was  Hidden", with: notification)
    }

자 이제, 키보드가 표시될 때 마다, 해당 함수들이 호출이 된다.
그럼 다음으로 넘어가자.

2. 호출된 키보드 이벤트 함수에서 키보드의 크기를 알아야, 다른 컴포넌트의 위치를 조정할 수 있다.

이벤트로 전달되는 NSNotification 클래스의 userInfo에서 해당 내용을 가져와야 알 수 있습니다.
키보드가 표시되는 Frame의 정보는 시작할 때와 표시가 다 되었을 때, 어디에 위치할지 알아낼 수 있습니다.
userInfo를 가져와서 description을 출력해 보면 아래와 같습니다.

NSConcreteNotification 0x610000242460 {name = UIKeyboardWillShowNotification; userInfo = {
    UIKeyboardAnimationCurveUserInfoKey = 7;
    UIKeyboardAnimationDurationUserInfoKey = "0.25";
    UIKeyboardBoundsUserInfoKey = "NSRect: {{0, 0}, {375, 258}}";
    UIKeyboardCenterBeginUserInfoKey = "NSPoint: {187.5, 796}";
    UIKeyboardCenterEndUserInfoKey = "NSPoint: {187.5, 538}";
    UIKeyboardFrameBeginUserInfoKey = "NSRect: {{0, 667}, {375, 258}}";
    UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 409}, {375, 258}}";
    UIKeyboardIsLocalUserInfoKey = 1;
}}

위에서 표시되는 각 UserInfoKey에 대해서 조금 더 자세히 봅시다.
  • UIKeyboardAnimationCurveUserInfoKey : 키보드가 표시될 때 Animation Curve
  • UIKeyboardAnimationDurationUserInfoKey : 키보드 표시되는 시간
  • UIKeyboardBoundsUserInfoKey : 키보드의 Bounds로 크기를 알 수 있는 bound
  • UIKeyboardCenterBeginUserInfoKey : 키보드 표시되기 시작할 때의 중심 Point
  • UIKeyboardCenterEndUserInfoKey : 키보드 표시된 후의 중심 Point
  • UIKeyboardFrameBeginUserInfoKey : 키보드가 표시되기 시작할 때의 frame
  • UIKeyboardFrameEndUserInfoKey : 키보드가 표시된 후의 Frame
  • UIKeyboardIsLocalUserInfoKey : 지금 표시되는 키보드가 Current App에 속한 것인지 True/False로 가지고 있음. iPad의 경우 여러 App이 화면에 표시될 수 있는데, 다른 App이 표시하는 키보드인지 내가 표시한 키보드인지 구분할 수 있음. 이것이 False이면 반응하면 안됨. (iOS 9 이상에서 지원)
아래 처럼 각 호출되는 함수에서 정보를 출력해 봅니다.

SourceCode

    fileprivate func info(name str:String, with notification:NSNotification) {
        if let userInfo = notification.userInfo {
            let frameBegin = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue ?? CGRect.zero
            let frameEnd = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue ?? CGRect.zero
            let curve = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber ?? NSNumber.init(value: 0)
            let duration = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber ).doubleValue
            print("\(str) (\(Int(frameBegin.origin.x)),\(Int(frameBegin.origin.y)),\(Int(frameBegin.size.width)),\(Int(frameBegin.size.height))), (\(Int(frameEnd.origin.x)),\(Int(frameEnd.origin.y)),\(Int(frameEnd.size.width)),\(Int(frameEnd.size.height))) curve:\(curve), duration:\(duration)")
        }
    }



3. 결과로 나오는 값은 어떻게 될까요?


iPhone7
세로
Keyboard Will beShown (0,667,375,258), (0,409,375,258) curve:7, duration:0.25
Keyboard Was  Shown    (0,667,375,258), (0,409,375,258) curve:7, duration:0.25
Keyboard Will beHidden (0,409,375,258), (0,667,375,258) curve:7, duration:0.25
Keyboard Was  Hidden   (0,409,375,258), (0,667,375,258) curve:7, duration:0.25
가로
Keyboard Will beShown (0,375,667,194), (0,181,667,194) curve:7, duration:0.25
Keyboard Was  Shown    (0,375,667,194), (0,181,667,194) curve:7, duration:0.25
Keyboard Will beHidden (0,181,667,194), (0,375,667,194) curve:7, duration:0.25
Keyboard Was  Hidden   (0,181,667,194), (0,375,667,194) curve:7, duration:0.25

위 결과를 보시면, 표시되기 전에는 화면 아래 부분에 있다가 표시되면, 위로 올라오게 됩니다.
가로/세로 모드일때 따라서, 키보드의 위치와 크기가 달라집니다.

위 정보를 이용해서, UIViewController에서  ToolBar나 TextField가 키보드 아래에 있을 때, 키보드가 표시될 때 위로 이동시켜서 화면에 표시할 수 있고, 키보드가 사라질 때, 원래 위치로 이동할 수 있습니다.

2016/06/30

Swift에서 Singletons 구현하기. (Singletons in Swift)


Swift에서 Class를 싱글톤으로 구현하기 위해서는 접근자를 Private로 막아서 별도로 생성하지 못하도록 해야 합니다.
그리고, sharedInstance를 클래스 변수로 두어서, 이 변수를 통해서만 instance를 읽어 가도록 하면 되겠습니다.
final class SingletonObject {
    static let sharedInstance = SingletonObject()
    private init() {
    }
}

이 클래스를 읽어서 쓸 경우는 아래 같이 가져오면 되겠습니다.

    let theInstance = SingletonObject.sharedInstance 



원본: http://www.thomashanning.com/singletons-in-swift/

2015/02/13

[iOS] OpenSSL을 iOS용으로 컴파일하기.

단계별로, 직접 컴파일 하는 방법과, build script를 이용해서 자동으로 하는 방법이 있습니다.

1. 자동으로 하는 방법

 웹사이트 :  https://github.com/x2on/OpenSSL-for-iPhone
 위 웹사이트에서 소스를 내려받습니다. (Download Zip)
 터미널에서 build-libssl.sh 를 실행하면, 자동을 만들어 줍니다.

 5개의 lib를 만들고, 그것을 하나로 합쳐서, universal lib로 만들어줍니다.
 해당 라이브러리는 /lib 밑에 libcrypto.a, libssl.a로 생성이 되고, /include에 헤더파일을 만들어 줍니다.
 사용하기 위해서는 lib에 있는 2개의 lib를 프로젝트에 추가하고, include를 프로젝트에서 찾을 수 있도록 추가해주면 됩니다.

2. 직접 컴파일하는 방법

- 소스 구하기.

  : 웹사이트(www.openssl.org)에서 버전을 내려 받습니다. 웹사이트에서 받거나 curl을 이용해서 받을 수 있습니다. 다운받을 버전은 1.0.1l 버전입니다.

DavidBaeui-iMac:Test DavidBae$ curl -O https://www.openssl.org/source/openssl-1.0.1l.tar.gz
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 4326k  100 4326k    0     0   279k      0  0:00:15  0:00:15 --:--:--  820k


- 받은 파일 압출 풀기.

  : Finder에서 풀거나, tar로 압축 풀기.

DavidBaeui-iMac:Test DavidBae$ mkdir src
openssl-1.0.1l.tar.gz src
DavidBaeui-iMac:Test DavidBae$ tar zxf openssl-1.0.1l.tar.gz -C /Users/DavidBae/Downloads/OpenSSL/Test/src
 이제 소스는 src 에 들어 있습니다.

- 소스에서 내용을 수정합니다.

//파일이름: crypto/ui/ui_openssl.c
//413 line
//static volatile sig_atomic_t intr_signal; //sig_atomic_t를 int로 변경
static volatile int intr_signal;
 : 소스의 413라인 쯤에서, sig_atomic_t 를 int로 변경해야 합니다.

소스가 준비가 되었으면, 총 5가지 타겟으로 컴파일을 하게 됩니다.
먼저, 시뮬레이터에서 사용할 32bit(i386), 64bit(x86_64)버전,
그리고, 실제 iOS에서 사용할 armv7, armv7s, arm64 버전을 각각 만듭니다.
아래에서 각각 만들고 마지막에 만들어진 5개를 하나의 lib로 묶는 작업을 합니다.


- i386 타겟으로 컴파일하기.

결과를 저장할 디렉토리는 현재 디렉토리의 bin/iPhoneSimulator8.1-i386.sdk 입니다.
물론 만들어놔야 겠지요.

./Configure 명령으로 Makefile을 만듭니다. iphoneos-cross 옵션과, 결과를 저장할 곳을 지정합니다.
DavidBaeui-iMac:openssl-1.0.1l DavidBae$./Configure iphoneos-cross --openssldir=/Users/DavidBae/Downloads/OpenSSL/Test/bin/iPhoneSimulator8.1-i386.sdk
..
..
make[1]: Nothing to be done for `links'.
generating dummy tests (if needed)...
make[1]: Nothing to be done for `generate'.

Configured for iphoneos-cross.
DavidBaeui-iMac:openssl-1.0.1l DavidBae$ //<--make파일이 다 만들어졌습니다.

이제 make파일을 수정해야합니다.
makefile을 열어서 아래부분을 수정합니다.

CC = /Applications/Xcode.app/Contents/Developer/usr/bin/gcc -arch i386
//CFLAG= -DOPENSSL_THREADS.....
CFLAG= -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator8.1.sdk -miphoneos-version-min=7.0 -DOPENSSL....//원래 있던 옵션은 그대로 연결..

makefile내부의 CFLAG에 -isysroot 옵션을 추가해 줍니다. makefile을 저장합니다.
Make를 실행합니다.

DavidBaeui-iMac:openssl-1.0.1l DavidBae$ make
.....
making all in tools...
make[1]: Nothing to be done for `all'.
DavidBaeui-iMac:openssl-1.0.1l DavidBae$ make install
..
..
cp openssl.pc /Users/DavidBae/Downloads/OpenSSL/Test//bin/iPhoneSimulator8.1-i386.sdk/lib/pkgconfig
chmod 644 /Users/DavidBae/Downloads/OpenSSL/Test//bin/iPhoneSimulator8.1-i386.sdk/lib/pkgconfig/openssl.pc
DavidBaeui-iMac:openssl-1.0.1l DavidBae$
//완료되었음. bin/iPhoneSimulator8.1-i386.sdk 아래에 보면 있습니다.
결과는 /User/DavidBae/Download/OpenSSL/Test/bin/iPhoneSimulator8.1-i386.sdk 디렉토리에 들어 있습니다.
 여기에 있는 lib는 simulator의 32비트 환경에서만 동작하게 됩니다.
 다른 타겟으로도 컴파일하고, 이것과 같이 합쳐두어야 모든 플랫폼에서 사용할 수 있는 universal library가 됩니다.

- x86_64 타겟으로 컴파일하기.

결과를 저장할 디렉토리는 현재 디렉토리의 bin/iPhoneSimulator8.1-x86_64.sdk 입니다.
물론 만들어놔야 겠지요.

./Configure 명령으로 Makefile을 만듭니다. 옵션으로 darwin64-x86_64-cc가 사용되어 i386과는 다르게 설정합니다. 출력할 디렉토리도 같이 설정해 줍니다.
DavidBaeui-iMac:openssl-1.0.1l DavidBae$ ./Configure darwin64-x86_64-cc --openssldir=/Users/DavidBae/Downloads/OpenSSL/Test//bin/iPhoneSimulator8.1-x86_64.sdk
...
...
generating dummy tests (if needed)...
make[1]: Nothing to be done for `generate'.

Configured for darwin64-x86_64-cc.
DavidBaeui-iMac:openssl-1.0.1l DavidBae$

makefile 변경하기.
CC = /Applications/Xcode.app/Contents/Developer/usr/bin/gcc -arch x86_64
//CFLAG= -DOPENSSL_THREADS.....
CFLAG= -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator8.1.sdk -miphoneos-version-min=7.0 -DOPENSSL....//원래 있던 옵션은 그대로 연결..

makefile내부의 CC를 변경하고, CFLAG에 -isysroot 옵션을 추가해 줍니다. makefile을 저장합니다.
Make를 실행합니다.
DavidBaeui-iMac:openssl-1.0.1l DavidBae$ make
.....
making all in tools...
make[1]: Nothing to be done for `all'.
DavidBaeui-iMac:openssl-1.0.1l DavidBae$ make install
..
..
cp openssl.pc /Users/DavidBae/Downloads/OpenSSL/Test//bin/iPhoneSimulator8.1-x86_64.sdk/lib/pkgconfig
chmod 644 /Users/DavidBae/Downloads/OpenSSL/Test//bin/iPhoneSimulator8.1-x86_64.sdk/lib/pkgconfig/openssl.pc
DavidBaeui-iMac:openssl-1.0.1l DavidBae$
//완료되었음. bin/iPhoneSimulator8.1-x86_64.sdk 아래에 보면 있습니다.


bin의 iPhoneSimulator8.1-x86_64.sdk의 lib와 include에 파일이 생성이 됨.

- armv7 타겟으로 컴파일하기.

결과를 저장할 디렉토리는 현재 디렉토리의 bin/iPhoneSimulator8.1-x86_64.sdk 입니다.
물론 만들어놔야 겠지요.

./Configure 명령으로 Makefile을 만듭니다. 옵션으로 darwin64-x86_64-cc가 사용되어 i386과는 다르게 설정합니다. 출력할 디렉토리도 같이 설정해 줍니다.

DavidBaeui-iMac:openssl-1.0.1l DavidBae$ ./Configure iphoneos-cross --openssldir=/Users/DavidBae/Downloads/OpenSSL/Test/bin/armv7/
...
...
generating dummy tests (if needed)...
make[1]: Nothing to be done for `generate'.

Configured for iphoneos-cross.

DavidBaeui-iMac:openssl-1.0.1l DavidBae$ 

makefile 변경하기.
CC = /Applications/Xcode.app/Contents/Developer/usr/bin/gcc -arch armv7
//CFLAG= -DOPENSSL_THREADS.....
CFLAG= -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.1.sdk -miphoneos-version-min=7.0 -DOPENSSL....//원래 있던 옵션은 그대로 연결.. CFLAG에 다른 -isysroot가 있는지 확인이 필요함.

makefile내부의 CC를 변경하고, CFLAG에 -isysroot 옵션을 추가해 줍니다. makefile을 저장합니다.
Make를 실행합니다.
DavidBaeui-iMac:openssl-1.0.1l DavidBae$ make
.....
making all in tools...
make[1]: Nothing to be done for `all'.
DavidBaeui-iMac:openssl-1.0.1l DavidBae$ make install
..
..
cp openssl.pc /Users/DavidBae/Downloads/OpenSSL/Test//bin/armv7/lib/pkgconfig
chmod 644 /Users/DavidBae/Downloads/OpenSSL/Test//bin/armv7/lib/pkgconfig/openssl.pc
DavidBaeui-iMac:openssl-1.0.1l DavidBae$
//완료되었음. bin/armv7 아래에 보면 있습니다.

bin/armv7/lib와 include에 파일이 생성이 됨.

- armv7s 타겟으로 컴파일하기.
: armv7을 armv7s로 변경해서, 진행

- arm64 타겟으로 컴파일하기.
: arm7s를 arm64로 변경해서 진행


- Universal Library 만들기

 만들어진 5개의 lib를 하나로 합친다.

DavidBaeui-iMac:openssl-1.0.1l DavidBae$ lipo -create /Users/DavidBae/Downloads/OpenSSL/Test/bin/i386/lib/libssl.a /Users/DavidBae/Downloads/OpenSSL/Test/bin/x86_64/lib/libssl.a /Users/DavidBae/Downloads/OpenSSL/Test/bin/armv7/lib/libssl.a /Users/DavidBae/Downloads/OpenSSL/Test/bin/armv7s/lib/libssl.a /Users/DavidBae/Downloads/OpenSSL/Test/bin/arm64/lib/libssl.a -output /Users/DavidBae/Downloads/OpenSSL/Test/lib/libssl.

DavidBaeui-iMac:openssl-1.0.1l DavidBae$ lipo -create /Users/DavidBae/Downloads/OpenSSL/Test/bin/i386/lib/libcrypto.a /Users/DavidBae/Downloads/OpenSSL/Test/bin/x86_64/lib/libcrypto.a /Users/DavidBae/Downloads/OpenSSL/Test/bin/armv7/lib/libcrypto.a /Users/DavidBae/Downloads/OpenSSL/Test/bin/armv7s/lib/libcrypto.a /Users/DavidBae/Downloads/OpenSSL/Test/bin/arm64/lib/libcrypto.a -output /Users/DavidBae/Downloads/OpenSSL/Test/lib/libcrypto.a

DavidBaeui-iMac:openssl-1.0.1l DavidBae$

OpenSSL에 대한 라이브러리가 생성이 되었다.

1번에서 build-libssl.sh 을 실행하면, 2번을 했던거와 같은 방법으로, 라이브러리를 만들어 준다.

잘 사용하시길....



2015/01/20

[iOS] CoreData에서 Entity의 개수를 효율적으로 읽어오기.

먼저 TableViewController의 경우, NSFetchedResultController를 사용하면 가장 효율적으로 관리할 수 있습니다.
특정 개수만 로딩 시켜서, 화면에 표시하므로 모든 데이터를 가져올 필요가 없게 됩니다.

하지만, 특정 경우, 지금 그 Entity의 개수가 몇개인지 알아야 할 경우가 있습니다.
이런때, 해당 Entity가 데이터가 크면, 그 개수 만큼의 NSManagedObject가 만들어지고, 데이터도 다 로딩 되므로, 순간 메모리 사용량이 커지게 됩니다.

이 경우, NSFetchRequest의  setIncludeSubentities를 NO로 해서 subentity들이 로딩 되지 않도록 하고,
context의 countForFetchRequest를 사용해서 해당되는 개수만 읽어 올 수 있게 됩니다.

참조: StackOverflow: Cocoa Core Data efficient way to count entities!

[iOS] unwind 함수를 코딩으로 호출하기.

Storyboard에서 Exit할 수 있는 Unwind segue를 만든다.
- 버튼에서 Exit로 Ctrl-Drag에서 만들 수도 있고
- 해당 View Controller의 아이콘에서 Exit로 Ctrl-Drag해서 Unwind segue를 만든다.

왼쪽 리스트에서 Unwind Segue from XXViewController를 선택하고, 오른쪽 Attribute Inspector에서 Identifier를 설정할 수 있다.

그러면,  코드상에서 performSegueWithIdentifier로 호출할 수 있다.

[참고: stackoverflow - How to perform Unwind segue programmatically?]


2014/12/26

[iOS] 원형의 그라디언트 이미지를 그려봅시다. (Rendering a radial gradient)

Radial Gradient

원형으로 배경을 은은하게 채워야하는 경우에, 아래와 같이 그림이 필요합니다.
이미지를 만들어서 넣으면 좋겠지만, Core Graphics를 사용해서 그릴 수 있습니다.
만들 이미지
source code
- (void)drawRadialGradient:(UIColor *)startColor
                  endColor:(UIColor *)endColor
                startPoint:(CGPoint)startPoint
               startRadius:(CGFloat)startRadius
                  endPoint:(CGPoint)endPoint
                 endRadius:(CGFloat)endRadius
                   context:(CGContextRef)context
{
    CGColorRef colorRef = startColor.CGColor;
    CGColorRef endColorRef = endColor.CGColor;
    NSArray *marrColors=[NSArray arrayWithObjects:
                         (__bridge id)colorRef,    //start color
                         (__bridge id)endColorRef, //end color
                         nil];
    CFArrayRef colors =(__bridge CFArrayRef)(marrColors);
    CGColorSpaceRef colorSpc = CGColorSpaceCreateDeviceRGB();
    CGGradientRef gradient = CGGradientCreateWithColors(colorSpc, colors, Nil);
    
    // generates Radial Gradient
    CGContextDrawRadialGradient(context, gradient,
                                startPoint, startRadius,
                                endPoint, endRadius,
                                0);
    CGColorSpaceRelease(colorSpc);
    CGGradientRelease(gradient);
}
 



이 함수를 이용해서, 중심에 그리면, 위 만들이미지와 같은 효과를 얻을 수 있습니다.

Drawing a radial gradient like Lens


그러면, Gradient에 이미지를 추가하면 중간에 이미지가 추가가 됩니다.
source code
- (void)drawRadialGradient:(UIColor *)startColor
                  midColor:(UIColor *)midColor
                  endColor:(UIColor *)endColor
                startPoint:(CGPoint)startPoint
               startRadius:(CGFloat)startRadius
                  endPoint:(CGPoint)endPoint
                 endRadius:(CGFloat)endRadius
                   context:(CGContextRef)context
{
    CGColorRef colorRef = startColor.CGColor;
    CGColorRef midColorRef = midColor.CGColor;
    CGColorRef endColorRef = endColor.CGColor;
    NSArray *marrColors=[NSArray arrayWithObjects:(__bridge id)colorRef,
                         (__bridge id)midColorRef,
                         (__bridge id)endColorRef, nil];
    CFArrayRef colors =(__bridge CFArrayRef)(marrColors);
    CGColorSpaceRef colorSpc = CGColorSpaceCreateDeviceRGB();
    CGGradientRef gradient = CGGradientCreateWithColors(colorSpc, colors, Nil);
    
    // generates Radial Gradient
    CGContextDrawRadialGradient(context, gradient,
                                startPoint, startRadius,
                                endPoint, endRadius,
                                0);
    CGColorSpaceRelease(colorSpc);
    CGGradientRelease(gradient);
}
호출하는 소스
- (void)drawRect:(CGRect)rect {
    
    CGContextRef context = UIGraphicsGetCurrentContext();

    
    // Drawing code
    UIColor *whiteColor = [UIColor whiteColor];
    UIColor *blueColor = [UIColor blueColor];
    UIColor *greenColor = [UIColor greenColor];
    
    CGSize size = self.frame.size;
    CGPoint center = CGPointMake(size.width/2, size.height/2);
    /*/
    [self drawRadialGradient:greenColor endColor:self.backgroundColor
                  startPoint:center startRadius:0
                    endPoint:center endRadius:size.width/2
                     context:context];
    /*/
    [self drawRadialGradient:greenColor midColor:blueColor endColor:whiteColor
                  startPoint:center startRadius:0
                    endPoint:center endRadius:size.width/2
                     context:context];
    CGPoint point2 = CGPointMake(size.width/2-15, size.height/2-15);
    [self drawRadialGradient:whiteColor endColor:greenColor
                  startPoint:point2 startRadius:0
                    endPoint:point2 endRadius:5
                     context:context];
    point2 = CGPointMake(size.width/2+15, size.height/2+15);
    [self drawRadialGradient:whiteColor endColor:greenColor
                  startPoint:point2 startRadius:0
                    endPoint:point2 endRadius:2
                     context:context];
     //*/
}


결과물
3가지 색으로만
3가지 색에, 점 2개 찍은것


나름 랜즈와 비슷한 것이 나온것 같군요.






2014/12/11

[iOS] NSString으로 된 값을 파일로 직접 저장하자.

현재 App의 디렉토리 Path를 읽어 오고, 거기에 파일 이름을 추가합니다.


 source code
- (NSURL *)urlForFilename:(NSString *)filename {
    NSFileManager *fm = [NSFileManager defaultManager];
    NSArray *urls = [fm URLsForDirectory:NSDocumentDirectory
                               inDomains:NSUserDomainMask];
    NSURL *directoryURL = urls[0];
    NSURL *fileURL = [directoryURL URLByAppendingPathComponent:filename];
    return fileURL;
}

이름을 정하고 나면, NSString에서 바로 저장합니다.


    BOOL status = [string writeToFile:[pathUrl.path stringByAppendingPathExtension:@"txt"]
                           atomically:YES 
                             encoding:NSUTF8StringEncoding 
                                error:&error];
        
    if (error != nil) {
        NSLog(@"save error: %@", [error description]);
    }
    if (status == NO) {
        NSLog(@"save error");
    }


파일로부터 데이터를 읽어 올 경우도, NSString의 카테고리를 이용합니다.

    NSString *string = [NSString stringWithContentsOfURL:pathUrl 
                                                encoding:NSUTF8StringEncoding 
                                                   error:&error];
    if (error != nil || string == nil) {
       NSLog(@"Can't load file: %@, error:%@", pathUrl.path, [error description]);
    }


만약, 문자열이 아닌 Binary데이터를 저장하려면, NSString대신, NSData의 카테고리 함수를 이용하면, 똑같이 사용이 가능합니다.