This document will be continuously updated.

문서가 너무 많아져서 30개로도 압축이 안되기 때문에 구글 문서로 모두 통합하여 업데이트 하기로 했다.

https://docs.google.com/document/d/1XeuDIhtXQ0UypbAG0yRRlsx1K0-LTWJaJr25snDvxRo/edit?usp=sharing

 

hjh_Swift

 

docs.google.com

 

뭘하던, 요구사항 정의와 도메인 정의, 데이터 저장 방법이 끝나야 하지. ㅡㅡ; 누더기 코드는 여기에만 두고 실제로는 모두 삭제한다. 한 달 쓰는(함께 일하는) 고가의 웹 개발자를 잘못 뽑았더니 데이터 정의를 못해서 ㅠㅠ 우왕좌왕 하다 결국 내가 하는... 그러나 생각해보면 DB 담당자가 없으면 웹 개발자가 해야 한다는 인식이 잘못되었다. 서비스에서 가장 많은 %를 개발하고 있는 개발자가 데이터 정의를 하는게 맞다는 교훈을 얻은 경험이었다.

print("Auth.auth().currentUser!.uid = " + (Auth.auth().currentUser?.uid ?? ""))

            print("app.current user = " + (app.currentUser?.id ?? ""))

            //            print("(Int(Date.timeIntervalSinceReferenceDate * 1000))/fromData/(imageURL.lastPathComponent) = " + "/\(Int(Date.timeIntervalSinceReferenceDate * 1000))/fromData/\(imageURL.lastPathComponent)")

//            let addr_firebase_storage = ViewsAppConfiguration.shared.addr_firebase_storage

            let storage = Storage.storage(url: addr_firebase_storage)

            //            let savingLocation = "gs://" + (self.udfirstArg ?? "") + "/" + "230830.jpeg"

            let thirdWords = self.udthirdArg as Any

            let stringValue = (thirdWords as? String) ?? "Default Value"

            print("Value as String: \(stringValue)")

            

            let businessNumber = self.udsecondArg

            let businessNumberWithoutDashes = (self.udsecondArg ?? "").replacingOccurrences(of: "-", with: "")

            // Now, businessNumberWithoutDashes contains the business number without dashes

            

            let savingLocation = addr_firebase_storage + "2023/" + (app.currentUser?.id ?? "") + "/" + (self.udfirstArg ?? "") + "/" + "/\(Int(Date.timeIntervalSinceReferenceDate * 1000))" + "_" + stringValue + "_" + businessNumberWithoutDashes + ".jpg"

            let storageRef = storage.reference(forURL: savingLocation)

            print("HJH_UserDefault logs(self.udfirstArg) = ", self.udfirstArg as Any)

            print("HJH_UserDefault logs(self.udsecondArg) = ", self.udsecondArg as Any)

            print("HJH_UserDefault logs(self.udthirdArg) = ", self.udthirdArg as Any)

            

            let metadata = StorageMetadata()

            //            metadata.contentType = "ProfileImage/jpeg"

            metadata.contentType = "image/jpeg"

            

핵심은 savingLocation 이다. 보통은 파일명 해시화가 가장 좋았지만 한 번(1초 내)에 쓰레드에서 1000개씩 돌린것을 구글과 몽고DB, 아마존에 보내다  보니 DB 메타데이터랑 꼬이는 현상이 있어 주요 정보는 줄이고, 파일명 자체로 정보를 보내며 싱크를 맞추기로 했다.

var udfirstArg = UserDefaults.standard.string(forKey: "CameraViewfirstArg") 유저 디폴트 키로 오픈 소스와 데이터를 연결한다. 

let savingLocation = addr_firebase_storage + "2023/" + (app.currentUser?.id ?? "") + "/" + (self.udfirstArg ?? "") + "/" + "/\(Int(Date.timeIntervalSinceReferenceDate * 1000))" + "_" + stringValue + "_" + businessNumberWithoutDashes + ".jpg"

가독성이 떨어질지도 모르지만 혼자 개발하다 보니 나에게는 이게 가장 가독성이 높다. 그리고 잘 짠다는 것은 코드 조각을 공개해도 주요 정보는 공개 되지 않는 시큐리티 정보 분할 방식 코딩이 잘 짠다는 것이다. 라인 맞출 필요 없이 formatter면 충분하고 실제 바이트 코드는 모두 리니어 하다. 사람의 두뇌 능력이 떨어지니 다 떨어뜨려서 이해를 해야하는 것 뿐.

주니어 개발자를 뽑으면 늘 리팩토링만 하다가 정작 신규 피처를 개발 못하고 욕하면서 나가며, 자신이 뭘 개선했니 마니 한다. 그래서 요즘엔 주니어 개발자 뽑으면 소스 공유 안하고 6개월 정도 똑같은 프로젝트 만들어 보라고 하고 시간을 준다. 그러면 스스로 굉장히 공부를 많이 한 줄 알고 6개월 동안 개고생하다가 배터리 다 되고, 본인 포트폴리오로 해서 이직을 한다. 이런 케이스를 한 6명 정도 겪다 보니 돈을 최소 2배 주더라도 시니어 개발자를 쓴다. 시니어 개발자와 커뮤니케이션은 간단하다. 그냥 전체를 떼 주고 VOC, 요구사항이나 기능, 퍼블리싱 시기 관련해서만 이야기를 한다. 개발 관리가 얼마나 편한지 깨닫게 되는 시간이다. 그리고 나도 개발자다보니 신규 개발 기술이나 어떤 것은 도전적인 것이고 어떤 것은 편한 것인지 이야기 하며, 좀 더 친해지다보면 우리 휴가 보전을 위해 좀 구린 개발자들이 모인 집단과 비교해서 충분한 개발 속도를 확보한 상태에서 개발 속도를 조절한다. 장기 휴가 아닌 장기 휴가를 가진다. 그게 큰 규모의 기업이던 작은 규모의 기업이던 스톡홀더나 경영자나 개발은 전혀 못하면서 자신이 뛰어난 관리자라고 착각하는 사람들을 대하는 방식이었고, 내가 대표가 되고 나서도 왠만해서는 경쟁력이 나오니 너무 아등바등 하지는 않는다. 시니어만 모인 조직만의 장점이다.

 그러나 가끔 서로 얼굴 보며 너무 재미 없다고 느낄 때쯤 개발자를 찾으면 오로지 인성만 보고 뽑는다. 개발 실력을 정말 어릴적 부터 닦아서 주변 사람들보다 개발을 너무 잘해서 나오는 자신감과 그런 실력으로 주변 사람들의 칭찬에 길러져서 흡사, 온실속의 밝은 화초가 아니라면, 사실 다 고만고만하게 보이는게 사실이다.

인성을 보는 방법은 쉽다. 에베레스트를 올랐던, 다른 분야에서 1등을 했었던. 정말 뭔가 열심히 해서 결과를 내려고 부단히 노력했었던 사람이라면 또 그것을 다른 사람을 위한 행동이었다면 개발 분야에서도 똑같이 적용된다.

우리 분야는 학문적으로 너무 들어가는 깊이를 항상 경계하기 때문에 그 경계가 풀리면 무한히 재미있는 세계가, 또 그런 세계가 여러개 기다리고 있다. 단점은 시간이 너무 빨리 가서 어느 순간 늙어 있는 자신을 발견하면 좀 벙찐다.

 

 

===== 화사 첨족 ==========

제주도 차 가지고 가는 방법

 

배 출발 후 바로 쓴다. 배타고 나서 할게 없다. ㅠ

 

수원에서 외롭게 운전해서 간다. 12시(자정) 출발했는데 첫번째 휴게소에서 잠들어서 두시까지 잤다.

 

 

 

 

 

다섯시 반에 도착. 두시간 잔거 빼면 세시간 반 걸린다. 고속도로로 다 이어져 있고 평일인걸 감안(정체현상 제로) 휴게소 시간 합하면 다섯시간 반 걸린다고 보자.

 

 

일곱시부터라고 되어있지만 여섯시 반부터 차를 싣는다. 걍 운전하고 들어가면 된다.

 

참고로 여수 엑스포 옆에 여객 터미널이 있다. 예약 안하고 왔어도 일단 차부터 싣는다. 난 예약했다. 평일은 예약 안해도 될 것 같다.(주말은 경험이 없으니 모름) 차를 배에 선적하고 화면에 보이는 사무실로 가서 계산 한다.(예약으로 결재한 사람도 무조건 들러야 한다)

 

 

 

 

 

이층 주차장이 안에 있다고 보면 된다. 안내하시는 분들이 많으니 가라는데로 가서 주차하면 된다. 바퀴 결박해주신다.

 

원래 가격은

 

이거다. 그런데 6월 차량 가지고 온 탑승객은 일등실 업글 이벤트를 했다. 땡잡음 ㅋㅋㅋ 일등실이랑 특등실은 2인 이상이기 때문에 혼자오면 2등실을 잡는게 맞다.(뭐 돈 ㅈㄹ해도 되긴 함)

 

뱃사람들 격하지만 인심은 잘 안다. 지나가면서 눈썰미를 발휘하니 이벤트 아니라도 어르신들은 다 업글 해주는 듯하다.(하지만 말할 수 없다는... 진짠가?)

 

배는 참 크다. 내부는

 

4인실이 이렇다. 밤새 운전했으니 자고 일어나면 제주도일 듯. 레스토랑도 있고 화장실에 비데도 있고 기타 등등 적을게 많다. 그러나 {제주도에 차 가져가기}는 충분히 이야기 한 듯. 참고로 세월호 여파로 사라진 해운업 회사 많다. 블로그 포스팅 믿지 말고 예약하려면 직접 선사와 연락해서 회사가 있는지 확인해야 한다. 여수보다 완도가 제주도에 가깝지만 운전하기가 싫었다.(여수 엑스포가 있으니 도로가 잘 되어있을거라 판단함) 고향 부산에서는 제주까지 7~8 시간 이라고 했다. 완도는 세네시간, 여수는 다섯 시간이라더라.

 

참고로 이 정도 파도에서는 어린이와 온다고 해도 배 멀미 걱정 안해도 되겠다. 흔들림은 거읭 없다. 시동 걸어놓고 정차된 자동차 안에 있는 기분.

 

이등실엔 이불이 없다 ㅠㅠ

 

 

 

 

밖에는 커다란 재떨이식 쓰레기통이 있다.

 

애연가 분들은 담배불 붙이기는 힘들어도 좋을 듯. 터보라이터로 준비하시길.

 

 

 

 

전화도 계속 잘 터진다. 대한민국만세. LTE 는 조금 느린 기분.

 

 

 

 

 

제주항으로 도착합니다. 두시에 항구 근접!

 

 

정박 직전에 밖에서 구경하고팠는데 엄청난 배기가스가 바람을 이용하여 얼굴을 때려 안으로 들어갔다.

 

 

 

 

차를 가지고 가실 분께 도움되셨길.

 

 

 

 

http://www.hanilexpress.co.kr/external/ticket/login?returnurl=reservation

Hanilexpress



www.hanilexpress.co.kr

 

 

2016 9월 업데이트

 

 

 

 

제주에서 여수로 가는 것은 10시 도착이더라. 목포 도착으로 하면 6시 30분쯤 온다고 한다. 똑같이 제주 4 부두고 12시 40분에 와서 차량 싣고 면세점 쇼핑하고 탔다. 입석인데 더 비쌌다.

 

그래도 제주 생활 정리하고 바다보고 바람을 맞으며 가는 이 길이 마냥 즐겁다. 경기도 계신 분들께는 목포로 복귀를 추천 드린다. 휴게실에 책 놔두고 옆에 못 앉게 하는 사람들 많던데.... 쫌...

 

 

 

 

바람이 워낙 쎄서 몸이 날아갈 것 같은데 덩달아 꿀꿀한 기분도 함께 날아가는 기분.

 

 

 

 

두시에 출발하니 골드스텔라가 들어온다. 골드스텔라는 네시 반 출발에 열시 여수 도착이라 수원까지 운전하려면 빡실듯

 

 

 

 

바다를 보다 두시간 20분쯤 지나면 풍경이 펼쳐진다.

 

 

7시에 도착. 보통은 6시 반 도착이라고 한다. 귀경길은 서해안 고속도로 타다가 공주쪽으로 빠졌다가 경부타고 왔는데 휴게소에서 밥 먹고 집에 11시에 집에 도착했다.

 

 

 

 

목포 출발 제주 도착은 밤에 출발한다고 들었다.

 

 

 

 

혹 제주도에 다시 차 가져갈 일 있으면 여수와 목포를 잘 이용하려고 한다.

 

 

'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
iOS UI 기초 - Swift UI  (2) 2019.01.03

[이 포스트는 계속 업데이트 합니다.] --> 중단되었습니다. 보통 포스팅이 중단되면 관련 기술이 완전히 사장되었던지, 다른 포스트에 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