2018년 10월 7일 일요일

Xcode에서 이미지 없이 둥근 버튼, 원 버튼 디자인 적용 하기

1. 다음과 같은 라운드 버튼을 이미지 없이





zeplin 오른쪽 사이드에 사각형 이라 표시되고 전혀 image asset에 대한 정보가 없다.
디자이너는 그냥 파란색 도형을 디자인 한것이다.

로그인 버튼은 사각형이며 파란색 도형인데 이미지가 없다?

이러한 경우는

UIVew를 상속 받은 UIButton에서 conerRadius를 설정 하여 처리 하면 된다.
결국 코딩으로 해야 한다.


2. 이번에는 속이 투명한 완전 원형 버튼






width / height 값이 동일하게 그리고 conerRadius를 width/2 로 설정 하면 원이 된다.
결국 코딩이다.

viewDidLoad에 코딩으로 하지 않고 스토리보드 인터페이스 빌더에서 속성으로 처리 하고 싶다면?


import UIKit

extension UIView {
    
    @IBInspectable var cornerRadius: CGFloat {
        get {
            return layer.cornerRadius
        }
        set {
            layer.cornerRadius = newValue
            layer.masksToBounds = newValue > 0
        }
    }
    
    @IBInspectable var borderWidth: CGFloat {
        get {
            return layer.borderWidth
        }
        set {
            layer.borderWidth = newValue
        }
    }
    
    @IBInspectable var borderColor: UIColor? {
        get {
            return UIColor(cgColor: layer.borderColor!)
        }
        set {
            layer.borderColor = newValue?.cgColor
        }
    }

}


위와 같이 extension을 작성 하면

@IBInspectable 덕에 IB에 표시된다.



extension은 @IBDesignable이 적용이 되지 않아 storyboard 랜더링은 그대로 사각형이지만 런타임에서는 훌륭한 타원으로 표출 된다.


2번 원 버튼의 경우 배경색을 투명으로 하고 border with  와 border color 를 적용 하고 corner radius를 width에 1/2로 적용 하면 원 버튼이 표현 가능 하다.





















Xcode storyboard 리팩토링

Main.storyboard에 ViewController를 계속해서 디자인 하다 보면
성능이 별로 좋지 않은 mac에서는 랜더링 속도가 현저하게 떨어진다.

ViewController에 image등을 올리는 작업이 늘어 나면 늘어 날 수록 더이상 편집 자체가
너무 느려 하기 어려워 질 수도 있다.


이렇게 하나의 Main.storyboard에 모든 ViewController를 때려 박지 않고
여러개의 storyboard 파일로 나누어 segue를 연결 할 수 있다.


스토리보드 리팩토링 방법

1. 새로운 스토리 보드 파일을 추가 한다.
2. 기존 Main.storyboard 파일과 새로 만든 스토리 보드 파일을 보기 좋게 그룹으로 생성 하여 추가 한다.
3. Main.storyboard 파일에서 일부 연결된 ViewController들을 선택 하여 Cmd + X로 자른다.
4. 새로 추가된 스토리 보드 파일에 붙여 넣기를 한다.
5. 새로 추가된 스토리 보드에 첫번째 ViewController를 is Initial View Controller로 체크 한다.
6. Main.storyboard 파일에서 새로 추가된 스토리 보드와 segue를 연결 한다.





Main.storyboard를 간단하게 바꾸고 segue에 연결은 스토리 보드 레퍼런스로 한다.


Xcode 10에서는 라이브러리 화면이 팝업으로 나오게 되어 있다. Cmd + Shift + L




두번째 항목이 스토리 보드 레퍼런스 이다.

이런한 과정을 통해 프로젝트 성격에 맡도록 storyboard 파일을 나누면
각각의 스토리 보드에서 적은수의 View Controller를 처리 하도록 하여 Xcode에서 보다 쾌적 하게 작업 할 수 있다.

2018년 9월 20일 목요일

iOS ViewController 그라데이션 배경색 적용 하기


위와 같이 zeplin에서 색상 정보가 표시되고 ViewController의 배경색을 적용 해야 하는 경우

디자이너님에게 배경 PNG를 주세요! 라고 할 수도 있고
코드로 해결 할 수도 있다.

위 두점은 그라데이션의 시작점이다.
그리고 두개의 색상 정보가 있다.

extension CAGradientLayer {
    
    convenience init(frame: CGRect, colors: [UIColor]) {
        self.init()
        self.frame = frame
        self.colors = []
        for color in colors {
            self.colors?.append(color.cgColor)
        }
        startPoint = CGPoint(x: 0, y: 0)
        endPoint = CGPoint(x: 1, y: 1)
    }
    
    convenience init(frame: CGRect, colors: [UIColor], startPoint: CGPoint, endPoint: CGPoint) {
        self.init()
        self.frame = frame
        self.colors = []
        for color in colors {
            self.colors?.append(color.cgColor)
        }
        self.startPoint = startPoint
        self.endPoint = endPoint
    }
    
    func createGradientImage() -> UIImage? {
        
        var image: UIImage? = nil
        UIGraphicsBeginImageContext(bounds.size)
        if let context = UIGraphicsGetCurrentContext() {
            render(in: context)
            image = UIGraphicsGetImageFromCurrentImageContext()
        }
        UIGraphicsEndImageContext()
        return image
    }

}

위코드에서 첫번째 convenience 초기화는 대각선 즉 0,0 ~ 1,1 로 그라데이션 을 적용 하는 것이고
추가로 두번째 convenience초기화는 startPoint와 endPoint를 파라메터로 전달 할 수 있도록 만든 초기화 이다.


위와 같이 extension을 만들고

실제 ViewController에서 imageView를 전체 영역으로 올리고 다음과 같이 코딩을 한다.

    @IBOutlet weak var backgroundImage: UIImageView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        
        var colors = [UIColor]()
        colors.append( colorLiteral(red: 0, green: 0.6941176471, blue: 1, alpha: 1))
        colors.append( colorLiteral(red: 0.05882352941, green: 0.8392156863, blue: 0.8078431373, alpha: 1))
        let gradientLayer = CAGradientLayer(frame: self.view.frame, colors: colors, startPoint: CGPoint(x: 0.5, y:0), endPoint: CGPoint(x:0.5, y:1))
        backgroundImage.image = gradientLayer.createGradientImage()


    }


colorLiteral을 사용해서 숫자로 표시되지만 실제로른 Xcode에서 색상 표를 이용해 hex로 입력 한것이다.


CGPoint(x: 0.5, y:0)  ~ CGPoint(x:0.5, y:1) 은 중앙 상단에서 중앙 하단을 의미 한다.













2018년 8월 17일 금요일

네비게이션 컨트롤의 rootViewController 변경하기

 앱 최초 실행시에 intro -> 가입유도 / 또는 로그인

로그인 성공이후는 main 으로 실행 되는 앱이 있다고 하면

어느 경우는 intro가 navigation controller의 rootViewController 이고

어느 경우는 main이 navigation controller의 rootViewCotnorller  가 된다.

main으로 계속 실행되던 앱에서 logout 기능을 이용하면 login viewController가

navigationViewController에 rootViewController가 되어 실행된다.


스토리 보드를 이용하고 segue를 잘 쓰려고 하는데 앱 최초 진입점이 다르고
navigation controller를 결국에는 storyboard Embed in 으로 해서 만들 수 도 없고

결국 코드로 ViewController를 storyboard에서 찾아다가 push 하고 navigation controller를 초기 화 하고 복잡하게 ... 스파게티...


이러한 앱을 개발 할때 다음과 같이 하면 좀 수월 하다.

1. navigation controller 는 storyboard에 만들지 않는다.

2. navigation controller에 root가 될 viewController 들 ( 2개 또는 3개) 는
attribute inspector에서 top bar 항목을 설정 해준다.

그래야 하위 segue가 연결된 viewController들이 네비바가 있는것처럼 디자인 할 수 있다.

3. init viewController 및 navgation controller의 rootController를 appdelegate.swift에서 구현 한다.



didFinishLaunchingWithOptions 메서드에서 UserDefaults 등으로 구현하여 현재 상황에 맡는 ViewController를 rootViewController로 하여 navigation Controller를 설정 하고 시작 한다.

//intro
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        let root = storyboard.instantiateViewController(withIdentifier: "IntroViewController")
        nvc = UINavigationController(rootViewController: root)


        window?.rootViewController = nvc

        window?.makeKeyAndVisible()


로그인 등이 완료되어 main으로 시작 하거나, 또는 로그 아웃으로 인해 다시 intro로 시작 해야 하는 경우를 대비해 
스위칭 하는 메서드를 appdelegate에 구현 한다.
func switchIntro() {
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        let vc = storyboard.instantiateViewController(withIdentifier: "IntroViewController")
        nvc = UINavigationController(rootViewController: vc)
        
        self.window?.rootViewController = nvc
        window?.makeKeyAndVisible()
    }
    
    func switchMain() {
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        let vc = storyboard.instantiateViewController(withIdentifier: "MainViewController")
        nvc = UINavigationController(rootViewController: vc)
        
        self.window?.rootViewController = nvc
        window?.makeKeyAndVisible()

    }



어떠한 ViewController에서 navigaion Controller의 루트를 변경 하고자 하면 다음과 같이 하면 된다.
let appDelegate = UIApplication.shared.delegate as! AppDelegate
            
            appDelegate.switchIntro()




2018년 6월 6일 수요일

iPhone 6 7 8 plus 에서 상태바가 크게 나오는 앱 올바른 스케일로 나오게 하려면?

실제로 오래전에 만들어진 프로젝트에서 코드를 추가하다 보니

플러스 모델에서 화면 스케일이 업 되어 표출 되는 경우를 발견 했다.

실제로 아직도 내가 사용하는 iphone 6+ 에서 사용되는 앱중에 이렇게 오래된 프로그램인 듯 보이는 앱들이 존재 한다.

내가 사용중인 앱중에는  증권통,  키움증권 영웅문  과 같은 앱들이 아직도 상태바가 크게 보인다. 마치 오래전에 만든 앱이 6+에서 그냥 화면이 늘어 나서 보이는 느낌이다.

대신 증권 앱이나 기업 은행 앱은 최근에 바로 잡힌 듯 하다.



위에부터 키움증권, 증권통, facebook 의 iPhone 6 +에서 스크린샷이다.

왜 이러한 현상이 나타날까?

실제로 오래전에 만들어진 앱에 코드를 추가하고 유지보수 하다가 이러한 현상을 바로 잡기 위해 프로젝트 설정을 해메고 찾고 하였지만 해답은 의외로 간단 했다.



Launch Screen 스토리 보드르 추가 하여 위와 같이 프로젝트를 설정하면
올바른 크기로 프로그램이 실행 된다.

다음은 스택오버플로우의 관련 글이다.
https://stackoverflow.com/questions/25754942/how-to-enable-native-resolution-for-apps-on-iphone-6-and-6-plus

알면 별것 아니고 모르면 개고생이다.

증권통앱에 이방법을 알려주고 싶다.

2018년 4월 18일 수요일

Xcode 9 에서 컬러 리터럴 사용하기

코드에서 색상 값을 UIColor()에 생성자에 RGB 값으로 코딩을 하면 무슨 색인지 코드만 보고는 알수가 없다.



Xcode에서는 이러한 점을 고려하여 위와 같이 color literal을 지원하게 되었다.
색상이 Xcode에디터에서 문자 처럼 표시 된다.

color lteral을 코드에 적용 하는 방법은 다음과 같다.

color literal을 타이핑 하면 다음과 같이 코드 완성이 이루어지려 한다.


에디터에서 색상을 더블클릭 하면 색상 선택 창이 나타난다.




Other를 선택 하여 RGB 및 alpha 값을 적용 하여 원하는 색상 리터럴 문자를 만들수 있다.




이렇게 코드를 작성하면 실제는 colorLiteral() 로 코딩되어 있고 Xcode editor 상에서는 색상으로 표시되어 가독성이 좋다.











2018년 4월 12일 목요일

Xcode 9 에서 markdown파일을 렌더링 하여 보기




Xcode에서 .md 파일을 추가 할수 있다.
하지만 기본은 그냥 편집 할 수 있게만 되고 미리보기라 든지 그런 기능이 없다.


위에글은 링크된 파일을 다운로드 받으면 Xcode 프로젝트가 있는데 Xcode로 오픈해 보면 정말 README.md 가 렌더링 되서 보여준다.

아무런 차이도 없는데 해당 프로젝트는 그렇게 보여준다.
위 리크에서 설명 하듯이 답은 .xcodesamplecode.plist 파일이 프로젝트 (패키지 내용보기) 내부 에 있는 것이다. 파인더에서도 디폴트는 보여지지 않기 때문에 터미널을 이용하여 ls -al 로 확인 하였다.

sparrow-mac-mini:ARKitExample.xcodeproj sparrow$ ls -al
total 56
drwxr-xr-x@ 6 sparrow  staff    204  4 12 22:18 .
drwxr-xr-x@ 9 sparrow  staff    306  4 12 22:03 ..
-rw-r--r--@ 1 sparrow  staff    182  8  8  2017 .xcodesamplecode.plist
-rw-r--r--  1 sparrow  staff  24272  4 12 22:07 project.pbxproj
drwxr-xr-x@ 5 sparrow  staff    170  4 12 22:04 project.xcworkspace
drwxr-xr-x  3 sparrow  staff    102  4 12 22:04 xcuserdata

현재 작업중인  프로젝트는 워크스페이스로 구성되어 있었는데 여러 포함된  프로젝트 파일중에 README.md 파일을 추가 했고 렌더링 된 결과가 나올 줄 알았지만 결과는 아무런 변화가 없다.

하여 잘못된 글로 생각 했다.

워크스페이스로 된 프로젝트에서는 .xcodesamplecode.plist 파일을 workspace 디렉토리에 넣어야 된다.

결국 이글은 정확하게 잘된다.

구지 ARKitExample.xcodeproj을 다운 받지 않고도

터미널에서 워크스페이스 파일이나 xcode 프로젝트 파일에 디렉토리에 들어가서 .xcodesamplecode.plist 파일을 생성 하면 될 것으로 보인다.

해당 파일의 냉용은 다음과 같다.

sparrow-mac-mini:ARKitExample.xcodeproj sparrow$ cat .xcodesamplecode.plist 
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array/>
</plist>


이파일이 프로젝트 디렉토리 (패키지내용보기) 또는 터미널에서 cd  진입 하여 .xcodesamplecode.plist (ARKitExample.xcodeproj) 에 있는 파일이나 직접 만들어 넣으면 xcode가 markdown 파일을 렌더링 해서 보여준다.

단! 렌더링 되면 수정 xcode에서는 수정이 되지 않는다.

Visual Studio Code로 편집 수정하여 Xcode 프로젝트에 추가하면 멋진 도큐먼트를 포함하는 프로젝트가 될 것이다.

그리고 표문법은 적용 되지 않는다.

그래도 이정도면 쓸만 하다.