[이 포스트는 계속 업데이트 합니다.] --> 중단되었습니다. 보통 포스팅이 중단되면 관련 기술이 완전히 사장되었던지, 다른 포스트에 version 2가 있다는 의미 입니다.

 

2024년 1월 1일의 결정 : 단 하나의 프로젝트는 제외하고, 내가 혼자서 만드는 모든 프로젝트 SwiftUI 로 전격 교체!

내가 혼자서 만든 TakeTouch 는 바꾸지 못한다(내재화한 오픈소스가 방대하여 아마 8명 팀이 해도 1년은 넘게 걸릴거라 생각한다.)

 

 

 

==== 지난 글 ====

 

iOS에 대한 관심이 높아진 것인지? ^^;; 인기 급 상승하네. 이 포스팅은 내가 봐도 참 알차다.

---------------------------------------------------------------



1. 폰트 지정


label.font = UIFont.init(name: "NanumSquareOTFL", size: 16)

폰트 이름은

for family in UIFont.familyNames.sorted() {
    let names = UIFont.fontNames(forFamilyName: family)
    print("Family: \(family) Font names: \(names)")
}

샌프란시스코 폰트 다운로드(sf compact, sf pro)
https://developer.apple.com/fonts/

Fonts - Apple Developer
developer.apple.com 

나머진

https://brunch.co.kr/@jade/203

저작권 걱정없는 무료 한글폰트


한글날 기념 무료 한글 글꼴 포함 | 이제 매년 한글날이 되면 많은 기업들이 한글폰트를 내는 것이 좋은 홍보의 수단이 되는 듯 하다. 최근에 누구나 이용할 수 있도록 공개된 무료 한글 공개 폰트(글꼴)들을 정리해 보았다. 유료가 아니어도 예쁜 폰트가 참 많아졌다. 1. 네이버 나눔한글폰트 http://hangeul.naver.com/2016/nanum 가장 많이 사용하는 무료 한글 폰트가 아


brunch.co.kr/@jade/203 

2. UINavigationBar

색상 바꾸기

internal extension UIColor {
    convenience init(red: Int, green: Int, blue: Int) {
    assert(red >= 0 && red <= 255, "Invalid red component")
    assert(green >= 0 && green <= 255, "Invalid green component")
    assert(blue >= 0 && blue <= 255, "Invalid blue component")
    self.init(red: CGFloat(red) / 255.0, green: CGFloat(green) / 255.0, blue: CGFloat(blue) / 255.0, alpha: 1.0)
}

convenience init(rgb: Int) {
    self.init(
    red: (rgb >> 16) & 0xFF,
    green: (rgb >> 8) & 0xFF,
    blue: rgb & 0xFF
)}


override func viewDidLoad() {

    super.viewDidLoad()
    let navigationBarAppearance = UINavigationBar.appearance()
    navigationBarAppearance.tintColor = UIColor(rgb:0xffffff)
    navigationBarAppearance.barTintColor = UIColor(rgb:0x00aaee)
    navigationBarAppearance.backgroundColor = UIColor(rgb:0x00aabb)
}

숨기기

override func viewWillAppear(_ animated:Bool) {
    super.viewWillAppear(animated)
    UINavigationBar.appearance().isHidden = true
    self.navigationController!.isNavigationBarHidden = true
    self.navigationController?.navigationBar.isHidden = true
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    UINavigationBar.appearance().isHidden = false
}

3. iPhone Screen Resolution enum

//Device Native Resolution(Pixels) UIKit Size (Points) Native Scale factor UIKit Scale factor
//iphone XS Max 1125 x 2436 414x896
//iphone XS 1242 x 2688 375 x 812
//iPhone XR 828 x 1792 414x896
//iPhone X 1125 x 2436 375 x 812 3.0 3.0
//iPhone 8 Plus 1080 x 1920 414 x 736 2.608 3.0
//iPhone 8 750 x 1334 375 x 667 2.0 2.0
//iPhone 7 Plus 1080 x 1920 414 x 736 2.608 3.0
//iPhone 6s Plus 1080 x 1920 375 x 667 2.608 3.0
//iPhone 6 Plus 1080 x 1920 375 x 667 2.608 3.0
//iPhone 7 750 x 1334 375 x 667 2.0 2.0
//iPhone 6s 750 x 1334 375 x 667 2.0 2.0
//iPhone 6 750 x 1334 375 x 667 2.0 2.0
//iPhone SE 640 x 1136 320 x 568 2.0 2.0
//iPad Pro 12.9-inch
//(2nd generation) 2048 x 2732 1024 x 1366 2.0 2.0
//iPad Pro 10.5-inch 2224 x 1668 1112 x 834 2.0 2.0
//iPad Pro (12.9) 2048 x 2732 1024 x 1366 2.0 2.0
//iPad Pro (9.7-inch)1536 x 2048 768 x 1024 2.0 2.0
//iPad Air 2 1536 x 2048 768 x 1024 2.0 2.0
//iPad Mini 4 1536 x 2048 768 x 1024 2.0 2.0

extension CGPoint : ExpressibleByStringLiteral {
    public init(stringLiteral value: String) {
    let point = CGPointFromString(value)
    self.init(x: point.x, y: point.y)
    }
}

enum iPhoneScreen : CGPoint {
    case iPhone_X_XS = "{375, 812}"
    case iPhone_XSMax_XR = "{414, 896}"
    case iPhone_8Plus = "{414,736}"
    case iPhone_8_7_6sPlus_6s_6Plus_6 = "{375,667}"
    case iPhone_7_Plus = "{414, 736}"
    case iPhone_SE = "{320, 568}"
    case iPad_Pro_129 = "{1024, 1366}"
    case iPad_Pro_105 = "{1112, 834}"
    case iPad_Pro97_iPadAir2_iPadMini4 = "{768, 1024}"
}

쓸 때는 CGPoint 자체를 써도 되고, iPhoneScreen.iPad_Pro_129.rawValue.x 이렇게 써도 된다.



4. AUTOLAYOUT 맛보기

다음은 각 기 다른 회사의 채용 공고에 있는 iOS 개발자 자격 사항이다.


1. Objective-C, Swift, Auto Layout, Storyboard, 객체지향 등의 이해도가 있으신 분
2. swift, objective-c, auto layout
3. Swift , storyboard, auto layout 사용 경험자

Android 개발할 때는 FrameLayout 보다 RealativeLayout를 많이 쓰니 사실상 auto layout이 맞으나, 결국 아이콘은 xhdpi, xxhdpixxxhdpi 뿐 아니라, sw600dp, sw720dp 등 기억나는 것만 10개 이상의 layout 폴더에 각기 다른 pixel의 아이콘을 넣었었다. 그리고 기종을 체크해서 코드로 레이아웃을 다르게 구현했었다. 왕도는 없더라. 그래야 깨끗하게 나오니까. 삼성폰의 번들 아이콘이기도 했으나...

스타트업이나 중소기업을 보면 Andorid 보다 iOS로 시작하는 경우가 많다. 7~8년 전 내가 알던 안드로이드 폰 종류가 7000천 가지였는데 지금은 만가 지도 더 될 것 같다. 그래서 트러블 슈팅에서 오는 비용을 줄이기 위해 screen density가 단순한 iOS 먼저 개발하고 성공하면 Android를 하는 경우가 많았다. 같이 개발하더라도 Auto layout를 선호한다.

Android에서 autolayout 이 relative 스타일이라면, iOS는 constratins라 하겠다.


Constraints를 제대로 알려면


https://developer.apple.com/documentation/uikit/nslayoutconstraint





NSLayoutConstraint - UIKit | Apple Developer Documentation



developer.apple.com 
부터 보면 되겠지만


좀 더 쉽게 접근하려면 UITableView를 만들고 그 Cell을 코드로 구현하면 된다.


기초는 여기...
https://www.ioscreator.com/tutorials/prototype-cells-table-view-ios-tutorial-ios10
Prototype Cells in Table View iOS Tutorial


When using Table Views inside the Storyboard, prototype cells can be used to create some predefined or even custom layouts of the Table View Cells. In this tutorial we will create a basic Prototype cell, which includes an (optional) image and a title. This


www.ioscreator.com 











트러블 슈팅 팁.


unable to dequeue a cell with identifier
스토리 보드에서 Table View Cell의 Identifier 설정이 맞지 않는 경우.
let cell = tableView.dequeueReusableCell(withIdentifier: "여기", for: indexPath)
여기와 Identifier가 일치해야 한다.


굳이 cell을 붙이지 않더라도 TableView만 붙인 상태에서 테스트해 볼 수 있으니,
let cell: UITableViewCell = UITableViewCell(style:UITableViewCellStyle.subtitle, reuseIdentifier: "Cell")
이 코드로 테이블 뷰가 잘 보이는지 확인할 수 있다.


Could not cast value of type 'UITableViewCell'
self.tableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "여기")

즉, Constraints 만드는 방법은 2가지

contentView.addConstraint(NSLayoutConstraint(item: value, attribute: NSLayoutAttribute.centerY, relatedBy: NSLayoutRelation.equal, toItem: self.contentView, attribute: NSLayoutAttribute.centerY, multiplier: 1, constant: 0))


contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-[title]-[value]-|", options: [], metrics: nil, views: viewsDict))


다양한 옵션은

https://stackoverflow.com/questions/25413239/custom-uitableviewcell-programmatically-using-swift


Custom UITableViewCell programmatically using Swift


Hey all I am trying to create a custom UITableViewCell, but I see nothing on the simulator. Can you help me please. I can see the label only if I var labUserName = UILabel(frame: CGRectMake(0.0, 0...


stackoverflow.com 
를 참고하고 xcode의 autofix 기능을 이용하면 최신 코드로 변경이 가능하다.


맛을 봤다면, 이제 스냅킷을 쓰자.
https://github.com/SnapKit/SnapKit


SnapKit/SnapKit


A Swift Autolayout DSL for iOS & OS X. Contribute to SnapKit/SnapKit development by creating an account on GitHub.
github.com 



-
------------------- 나의 구글 블로그 글 옮겨서 통합 ------------------

 

Android에서 Androidmanifest.xml이 설계도 이 듯, iOS에서는 info.plist 가 비슷한 역할을 한다.

info.plist의 Launch screen interface file base name은 런치 스크린을

Main storyboard file base name은 처음 실행할 스토리 보드 이름을 지정한다.

 

스토리 보드도 xml 파일이다. 

 

<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14313.18" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">

 

처음 실행되는 스토리 보드는 initialViewController 체크박스에 체크를 해야 한다. 물론, GUI로 보여주지만 결국, 다음과 같이  XML이 바뀌는 것이다.

 

<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14313.18" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="QfJ-J6-o4q">

 

스토리 보드에서는 ViewController를 지정할 수 있다. 이제 어떻게 코드가 흐르는지 알 수 있다.

 

autolayout을 위해 스토리 보드에서 constraints를 지정할 수 있지만, 왜 굳이 코드로 해야 하는지는 깊게 설명하지 않고 싶지만 굳이 말하자면, 큰 프로그램이 되는 환경 속에서 UI 작업을 지속적으로 경험해보면 코드로 할 필요성을 느낄 수밖에 없다. 애플이 편하게 MVC로 만들어주긴 했지만 말이다.(안드로이드는 무조건 xml이지...)

개발자는 항상 플랫폼 테스터도 되기 때문에 (본디 디버깅은 재미있지만) 이후 재미없는 디버깅을 하게 될 가능성이 크다. 서버와의 싱크를 맞추려니 콜백 지옥에 빠지고 그 지옥에서 나로려니  MVC를  MVVM으로 바꿔야 한다. 결론은 코드라는 이야기.

 

초유의 관심사 AutoLayout, 1편에서 snapkit을 쓰자고 했다.

 

snapkit은 pod으로 설치하고

  pod 'SnapKit', '~> 4.0.0'

import SnapKit 만 하면 UIView와 그 하위 친구들에게 바로 쓸 수 있다.

 

var firstGroup : UIView 던

var btnVarious : UIButton이던

var iqr : UILabel이던

 

쉽게 constraints를 지정할 수 있다.

 

firstGroup.snp.makeConstraints { (make) in

            make.width.equalToSuperview()

            make.height.equalTo(29)

            make.left.equalTo(0)

        }

 

btnVarious.snp.makeConstraints { (make) in

            make.width.equalTo(29)

            make.height.equalTo(29)

            make.right.equalTo(btnQuestionMark.snp.left).offset(-1)

        }

 

iqr.snp.makeConstraints { (make) in

            make.width.equalTo(110)

            make.height.equalTo(28)

            make.left.equalToSuperview().offset(22)

        }

 

storyboard에서 만드는 것처럼 원하는 view에 add를 먼저 하고 제약사항을 만든다. 붙인 view가 바로 superview다. offset은 + 값과 -값이 있고 둘은 서로 반대 방향이다. 좌표계를 바꿀 수 있지만 보통은 좌측 상단이 0,0이니 어디가 + 방향 인지는 쉽게 알 수 있다.

 

     labelAmount.snp.makeConstraints { (make) in

            make.width.equalTo(52)

            make.height.equalTo(17)

            make.left.equalTo(amount.snp.left)

            make.top.equalTo(amount.snp.bottom)

        }

값을 직접 지정할 수동 있고, 다른 view의 snp를 얻어와서 맞춰줄 수도 있다.

 

그럼 어떤 값 지정이 가능할까? public class ConstraintMaker를 보면

    public var left: ConstraintMakerExtendable {

    public var top: ConstraintMakerExtendable {

    public var bottom: ConstraintMakerExtendable {

    public var right: ConstraintMakerExtendable {

    public var leading: ConstraintMakerExtendable {

    public var trailing: ConstraintMakerExtendable {

    public var width: ConstraintMakerExtendable {

    public var height: ConstraintMakerExtendable {

    public var centerX: ConstraintMakerExtendable {

    public var centerY: ConstraintMakerExtendable {

    public var baseline: ConstraintMakerExtendable {

    public var lastBaseline: ConstraintMakerExtendable {

    public var firstBaseline: ConstraintMakerExtendable {

    public var leftMargin: ConstraintMakerExtendable {

    public var rightMargin: ConstraintMakerExtendable {

    public var topMargin: ConstraintMakerExtendable {

    public var bottomMargin: ConstraintMakerExtendable {

    public var leadingMargin: ConstraintMakerExtendable {

    public var trailingMargin: ConstraintMakerExtendable {

    public var centerXWithinMargins: ConstraintMakerExtendable {

    public var centerYWithinMargins: ConstraintMakerExtendable {

    public var edges: ConstraintMakerExtendable {

    public var size: ConstraintMakerExtendable {

    public var center: ConstraintMakerExtendable {

    public var margins: ConstraintMakerExtendable {

    public var centerWithinMargins: ConstraintMakerExtendable {

 

이런 값들이 있고 스토리 보드를 쓰다 보면 거의 대부분 알 수 있는 것들이다. 주의할 점은 제약 사항 충돌이다. left와 right 제약 사항을 동시 지정해보면 쉽게 알 수 있을 것이다.

 

SnapKit의 원리는 간단하다. 다음과 같이 Int Values를 array에 넣고 나중에 for 문을 돌면서 UIKit의 API를 이용(1편 참조)하여 제약 사항을 지정하는 것이다.

 

    internal static var none: ConstraintAttributes { return 0 }

    internal static var left: ConstraintAttributes { return 1 }

    internal static var top: ConstraintAttributes {  return 2 }

    internal static var right: ConstraintAttributes { return 4 }

    internal static var bottom: ConstraintAttributes { return 8 }

 

10진수로 더해도 되지만, 본디 2진수의 각 자릿수가 더 편하다.

1, 2, 4, 8 이 각각 2진수에서 1, 10, 100, 1000이다. 그래서 2진수로 1111인 15의 값은

    internal static var edges: ConstraintAttributes { return 15 }

에 대입되어 있다.

 

size는 width, height다. 

 internal static var size: ConstraintAttributes { return 192 }

 

   internal static var width: ConstraintAttributes { return 64 }

   internal static var height: ConstraintAttributes { return 128 }

 

최고수는 

  internal static var centerWithinMargins: ConstraintAttributes { return 786432 }

라서 UInt(32비트에서만 2,147,483,647 64비트에서는 

18,446,744,073,709,551,615

... 믓튼 충분)에 담겨있다. 모든 수를 보려면(internal struct ConstraintAttributes : OptionSet... 참조)

 

이런 속성들을 

 

internal static func makeConstraints...

internal static func remakeConstraints...

internal static func updateConstraints...

internal static func removeConstraints(item: LayoutConstraintItem) {

        let constraints = item.constraints

        for constraint in constraints {

            constraint.deactivateIfNeeded()

        }

    }

 

를 이용하여 적용한다.

 

 internal func activateIfNeeded(updatingExisting: Bool = false) {

        guard let item = self.from.layoutConstraintItem else {

            print("WARNING: SnapKit failed to get from item from constraint. Activate will be a no-op.")

            return

        }

        let layoutConstraints = self.layoutConstraints

 

        if updatingExisting {

            var existingLayoutConstraints: [LayoutConstraint] = []

            for constraint in item.constraints {

                existingLayoutConstraints += constraint.layoutConstraints

            }

 

            for layoutConstraint in layoutConstraints {

                let existingLayoutConstraint = existingLayoutConstraints.first { $0 == layoutConstraint }

                guard let updateLayoutConstraint = existingLayoutConstraint else {

                    fatalError("Updated constraint could not find existing matching constraint to update: \(layoutConstraint)")

                }

 

                let updateLayoutAttribute = (updateLayoutConstraint.secondAttribute == .notAnAttribute) ? updateLayoutConstraint.firstAttribute : updateLayoutConstraint.secondAttribute

                updateLayoutConstraint.constant = self.constant.constraintConstantTargetValueFor(layoutAttribute: updateLayoutAttribute)

            }

        } else {

            

NSLayoutConstraint.activate(layoutConstraints)

            item.add(constraints: [self])

        }

    }

 

UI기초 전편의 링크를 보면 constratins를 적용하는 것이 번거로웠다.

 

contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-[image(10)]", options: [], metrics: nil, views: viewsDict))

contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[labTime]-|", options: [], metrics: nil, views: viewsDict))

contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-[username]-[message]-|", options: [], metrics: nil, views: viewsDict))

contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-[username]-[image(10)]-|", options: [], metrics: nil, views: viewsDict))

contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-[message]-[labTime]-|", options: [], metrics: nil, views: viewsDict))

 

혹은, 

 

        NSLayoutConstraint.deactivate([self])

        let newConstraint = NSLayoutConstraint(

            item: firstItem,

            attribute: firstAttribute,

            relatedBy: relation,

            toItem: secondItem,

            attribute: secondAttribute,

            multiplier: multiplier,

            constant: constant)

.

.

.

이런 식의...

 

그래서 쉬운 문법으로 AutoLayout을 위한 constraints를 쓸 수 있다는 점이 참 매력적인 SnapKit이다. , .snp.makeConstraints { (make) in ...    { $0. ...

 

'Swift & Python 실무 > {APP} SOCANNER APP' 카테고리의 다른 글

스토리보드 이동 방법  (0) 2019.02.14
제주도 항공 촬영  (0) 2019.01.19
C의 struct와 Swift 의 struct  (0) 2019.01.19
userDefault 활용  (0) 2019.01.16
Practical Swift  (0) 2019.01.14

+ Recent posts