SQLite로 데이터베이스를 활용하려다 Realm이라는 새로운 모바일 데이터베이스를 발견하고 이를 사용해 보기로 한다. 사용방법이나 튜토리얼이 모두 한글로 자세히 설명이 되어 있기에 금방 익숙해 질 수 있을 것 같다. 아래의 페이지를 참고하자.

 https://realm.io/kr/


 아직 자세히는 모르지만 직접 DB에 접속하여 스키마를 구현하는 것이 아니라, 개발 코드 내에서 Realm model의 Object를 상속받은 클래스를 정의하면 자동으로 DB에 해당 형태로 스키마를 구현하는 듯 하다. 즉 클래스의 프로퍼티를 정의하면 DB의 컬럼이 설정된다고 보면 될 것 같다. 그리고 SQL이 아니라 일반 객체의 메서드를 이용하는 것 처럼 DB의 데이터를 조작할 수 있다.


 이전에 만들었던 FoodVO를 대체하는 Realm 오브젝트 클래스를 만들어보자. 먼저 Realm을 사용하기 위한 framework를 Xcode에 추가해준다.




  Food.swift라는 오브젝트 클래스 파일을 만들고 아래와 같이 작성한다.


Food.swift

import Foundation
import RealmSwift

class Food: Object {
    
// Specify properties to ignore (Realm won't persist these)
    dynamic var name: String = ""
    dynamic var expDate: Date = Date(timeIntervalSinceNow: 0)
    private dynamic var categoryRawState = FoodCategory.Grain.rawValue
    public var category: FoodCategory {
        get {
            return FoodCategory(rawValue: categoryRawState)!
        }
        set {
            categoryRawState = newValue.rawValue
        }
    }
    
//  override static func ignoredProperties() -> [String] {
//    return []
//  }
}

enum FoodCategory : Int {
    case Grain, Potato, Sugars, Pulses, SeedsAndNuts, Vegetables, Mushrooms, Fruits, Meat, Eggs, Seafoods, Seaweed, MilkProducts, FatAndOils, Drinks, Seasoning, Etc
    static var count: Int { return FoodCategory.Etc.rawValue + 1 }
    
    var description: String {
        switch self {
        case .Grain : return "곡류"
        case .Potato : return "감자류"
        case .Sugars : return "당류"
        case .Pulses : return "두류"
        case .SeedsAndNuts : return "종실류"
        case .Vegetables : return "채소류"
        case .Mushrooms : return "버섯류"
        case .Fruits : return "과실류"
        case .Meat : return "육류"
        case .Eggs : return "난류"
        case .Seafoods : return "어패류"
        case .Seaweed : return "해조류"
        case .MilkProducts : return "유제품류"
        case .FatAndOils : return "유지류"
        case .Drinks : return "음료"
        case .Seasoning : return "조미료류"
        case .Etc : return "기타"
        }
    }
}

 데이터베이스에서는 enum 타입을 인식하지 못하므로, 실제 데이터베이스에 저장될 값은 private로 지정하여 Int 형태로 저장되게 하며, 코드 상에서만 enum타입으로 인식될 수 있도록 public 프로퍼티를 따로 설정하여 준다. enum타입인 FoodCategory를 해당 클래스에 동시에 선언해주었다. FoodCategory에는 단순 case만 입력한 것이 아니라 추가적으로 몇개의 데이터가 있는지 정적 변수를 추가하였으며, 각각에 대한 설명을 반환하는 변수 또한 추가하였다.



 이제 기존 코드를 수정하여 Realm 데이터베이스에 저장하고 값을 가져 올 수 있게 하자.


RefrigeratorTableViewController.swift

import UIKit
import RealmSwift

class RefrigeratorTableViewController : UITableViewController {
    var list = Array<Food>()
    var dateFormat = DateFormatter()
    
    override func viewDidLoad() {
        dateFormat.dateFormat = "yyyy-MM-dd"
        dateFormat.locale = Locale.init(identifier: "ko_KR")
        list.append(Food(value: ["name":"서울우유", "category":FoodCategory.MilkProducts, "expDate":dateFormat.date(from: "2017-04-20")!]))
        list.append(Food(value: ["name":"3분짜장", "category":FoodCategory.Etc, "expDate":dateFormat.date(from: "2017-07-31")!]))
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return list.count
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let row = list[indexPath.row]
        let cell = tableView.dequeueReusableCell(withIdentifier: "SokoCell")!
        cell.textLabel?.text = row.name
        cell.detailTextLabel?.text = "유통기한: \(dateFormat.string(from: row.expDate))"
        
        return cell
    }
    
    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        NSLog("Touch Table Row at %d", indexPath.row)
    }
    
    @IBAction func unwindToMainViewController(segue : UIStoryboardSegue) {
        
    }
    
    @IBAction func unwindToMainViewControllerAndRefreshList(seque : UIStoryboardSegue) {
        
    }

}


위 코드는 데이터베이스에 저장하는 코드가 아닌 Realm 객체로 테이블 뷰에 표시하는 코드이다. 이 코드로 실행이 되는지 테스트하고 진행한다.



'재료 추가' View Controller를 조금 더 완성시켜서 컴포넌트에 입력한 값을 저장할 수 있도록 만든다. 먼저 Swift파일을 하나 새로 생성해서 'AddFoodViewController.swift'라 이름 짓고 아래와 같이 storyboard의 ViewController와 연결한다. 또한 카테고리를 표현하는 PickerView도 추가한다.



 값을 읽어올 컴포넌트를 아래와 같이 연결한다.



 '닫기'와 '추가' 버튼을 unwind segue로 미리 RefrigeratorTableViewController에 생성한 unwindToMainViewController(segue:) 메서드로 연결한다. 그리고 '닫기'버튼의 Identifier를 'AddCancel'로, '추가'버튼은 'AddDone'으로 설정한다.




 코드를 아래와 같이 완성한다.


AddFoodViewController.swift

import UIKit

class AddFoodViewController : UIViewController, UIPickerViewDataSource, UIPickerViewDelegate {
    @IBOutlet var name: UITextField!
    @IBOutlet var expDate: UIDatePicker!
    @IBOutlet var category: UIPickerView!
    
    override func viewDidLoad() {
        
        // textField에 Custom디자인을 적용한 코드. 밑줄이 생성된다.
        let border = CALayer()
        let width = CGFloat(0.3)
        border.borderColor = UIColor.gray.cgColor
        border.frame = CGRect(x: 0, y: name.frame.size.height - width, width: name.frame.size.width, height: name.frame.size.height)
        
        border.borderWidth = width
        name.layer.addSublayer(border)
        name.layer.masksToBounds = true
    }
    
    // UIPickerView 설정 메서드
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 1
    }
    
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return FoodCategory.count
    }
    
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        return FoodCategory(rawValue: row)?.description
    }
    
    // 새 Food객체 생성 메서드
    func newFood() -> Food? {
        let food = Food(value: ["name":name.text!, "expDate":expDate.date])
        food.category = FoodCategory(rawValue: category.selectedRow(inComponent: 0))!
        return food
    }
    
    // unwind 하기 전 실행되는 메서드.
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "AddDone" {
            guard let food = newFood(), let refrigeratorTableViewController = segue.destination as? RefrigeratorTableViewController else {
                return
            }
            
            let realm = refrigeratorTableViewController.realm!
            try! realm.write {
                realm.add(food)
            }
        }
    }
}


 '추가' 버튼을 누를 때 컴포넌트에 설정된 값들이 저장되어야 한다. 따라서 unwind 하기 전에 prepare 메서드에서 DB에 저장하고, RefrigeratorTableViewController에서 DB가 수정됨을 확인하면 자동으로 화면을 새로고침한다. 이제 RefrigeratorTableViewController를 완성시키자.


RefrigeratorTableViewController.swift

import UIKit
import RealmSwift

class RefrigeratorTableViewController : UITableViewController {
    var list: Results<Food>?
    var dateFormat = DateFormatter()
    var notificationToken: NotificationToken!
    var realm: Realm!
    
    override func viewDidLoad() {
        dateFormat.dateFormat = "yyyy-MM-dd"
        dateFormat.locale = Locale.init(identifier: "ko_KR")
        setupRealm()
    }
    
    func setupRealm() {
        try! realm = Realm()
        
        func updateList() {
            if list == nil {
                list = self.realm.objects(Food.self)
            }
            self.tableView.reloadData()
        }
        updateList()
        
        self.notificationToken = self.realm.addNotificationBlock { _ in
            updateList()
        }
    }
    
    deinit {
        notificationToken.stop()
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return list!.count
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let row = list![indexPath.row]
        let cell = tableView.dequeueReusableCell(withIdentifier: "SokoCell")!
        cell.textLabel?.text = row.name
        cell.detailTextLabel?.text = "유통기한: \(dateFormat.string(from: row.expDate))"
        
        return cell
    }
    
    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        NSLog("Touch Table Row at %d", indexPath.row)
    }
    
    @IBAction func unwindToMainViewController(segue : UIStoryboardSegue) {
    }
}


 list타입을 원래의 Array<Food>타입이 아닌 Results<Food>타입으로 변경하였다. Results타입은 Realm 프레임워크에서 제공하는 타입으로 사용자가 Realm에서 가져온 데이터들의 배열로 Array와 유사합니다. 단 실제 DB데이터를 가지고 있는것과 같아서 이 배열에 값을 추가하므로써 DB에 값을 저장하는 것과 동일한 효과를 갖는다.

 setupRealm()에서 Realm 객체를 할당하고 Realm으로 부터 저장된 데이터들을 가져오는 updateList()메서드를 수행한다. 또한 notificationToken을 이용하여 Realm의 변화 여부를 감지한다.


 앱을 실행하여 Realm에 값이 저장되어 앱이 꺼지고 켜져도 값이 유지되는지 확인해보자.




 앱이 종료되었다가 다시 실행되어도 값이 유지되고 있기에 메모리에 유지된 것이 아닌 별도의 데이터베이스에 값이 저장되고 있는 것을 알 수 있다. Simulator의 앱 경로를 찾아 Realm Browser를 이용하여 Realm에 저장되고 있는지 확인 할 수 있다. (Realm Browser는 macOS에서만 제공된다.)


 Realm 파일의 경로는 아래와 같다. 아래의 파일을 Realm 브라우저로 실행하면 위와 같이 내용을 확인 할 수 있다.

/Users/<username>/Library/Developer/CoreSimulator/Devices/<simulator-uuid>/data/Containers/Data/Application/<application-uuid>/Documents/default.realm

 Xcode의 Debug 환경에서 아래의 LLDB 명령어를 입력하여 앱의 경로를 바로 찾을 수 있다.

(lldb) po Realm.Configuration.defaultConfiguration.fileURL

자세한 내용은 아래의 링크를 참고하자.

https://stackoverflow.com/questions/28465706/how-to-find-my-realm-file/28465803#28465803


 이 'soko' 앱의 핵심 기능은 "유통기한이 있는 음식물을 리스트로 만들어 관리하고, 필요시 알림을 해주는 것"이다. 그외의 기능은 결국 부가적인 기능이다. 따라서 핵심기능부터 완성해볼 것이다. 지난 탭 바로 나눠진 여러개의 View Controller 중에서 '내 냉장고'에 해당하는 탭의 기능을 구현해 본다.


 먼저 '내 냉장고'에 해당하는 View Controller를 클릭하고, 상단 메뉴의 [Editor] - [Embeded In] - [Navigation Controller]를 선택한다. 그럼 기존의 View Controller가 Navigation 형식으로 바뀌는 것을 볼 수 있다. 다른 View Controller에서도 Navigation Controller 형태로 만들 것이므로 모두 적용해 준다.




 Navigation Bar의 오른쪽에 생성된 ViewController를 선택하여 Delete키를 눌러 삭제하고, 오른쪽 하단의 Object library에서 Table View Controller를 끌어 화면에 놓고 Navigation Controller와 연결해주자.

 



 이제 Navigation Bar의 가운데 부분을 더블클릭 하면 제목을 입력할 수 있는 공간이 나타난다. 제목을 입력해 주자. 그리고 Xcode의 우측 하단의 [Show the Object library]에서 [Bar Button Item]을 찾아 Navigation Bar의 우측 상단에 놓아준다. 그리고 추가한 버튼을 클릭하여 Xcode 우측 상단의 [Show the Attribute Inspector]에서 System Item을 [Add]로 바꿔준다.

 






 

 이 상태에서 실행을 해 보면 다음과 같이 표시된다.


 목록이 존재하지 않는데도 여러개의 줄로 구분 되어있음을 알 수 있다. 목록이 없을 때에는 이러한 줄 구분이 안보이게 하기 위해 아무것도 존재하지 않는 View를 View Controller 안에 추가해 준다.


그럼 아래와 같이 내용이 없는 경우 줄이 표시되지 않는다.


 이제 테이블 뷰에 임의로 목록을 추가해 보자. 먼저 테이블 뷰의 Cell을 클릭한다. 화면 왼쪽의 Document Outline에서 직접 선택해 주어도 좋다. 그리고 우측의 [Attribute Inspector]에서 Identifier를 'SokoCell'로 바꾸고, Style을 'Subtitle'로 바꿔주었다.






 이렇게 설정한 셀은 실제로 화면에 표시되는게 아니라 서식을 만든 것 뿐이다. Custom형태로 서식을 만들 수 있으며 이는 기능이 완성된 후에 나중에 바꿀 것이다. 여러개의 셀을 Main.Storyboard에서 만드는게 아니라, 여기서 만든 서식을 코딩을 통해 여러개를 표시할 수 있도록 할 것이다. 왼쪽에서 'soko' 그룹에서 오른쪽 클릭하고 [New Group]을 누른 후, 생성된 그룹의 이름을 'refrigerator'로 만든다. 이 그룹에서는 '내 냉장고'에 해당하는 코드를 입력한 swift파일을 관리할 것이다.


 다시 refrigerator 그룹을 우클릭하고 [New File...]을 선택하고 Swift File을 선택한 후, 이름을 RefrigeratorTableViewController.swift로 만든다. 그리고 아래의 코드를 작성한다.


RefrigeratorTableViewController.swift

import UIKit

class RefrigeratorTableViewController : UITableViewController {

}

 위의 코드 안에서 이제 이 TableViewController를 조작하는 코드를 작성할 것이다.

 다시 Main.storyboard를 클릭하고, 우리가 생성한 TableViewController를 선택하고 우측 상단의 [Identity Inspector]에서 Class를 우리가 방금 만든 RefrigeratorTableViewController를 선택한다. 이렇게 Class를 선택하면 위에서 생성한 Swift파일과 Storyboard의 TableViewController가 연결 된 것이다.


 


 이제 직접 임의의 데이터를 넣어 화면에 출력시켜보자. 먼저 음식에 관한 데이터를 가지고 있는 ValueObject 클래스를 만든다.


FoodVO.swift

import Foundation

class FoodVO {
    init () {
    }

    var name : String?
    var sellByDate : Date?
    var category : FoodCategory?
}


FoodCategory라는 타입은 열거형 객체이다. 아래와 같이 선언하였다.


FoodCategory.swift

import Foundation

enum FoodCategory : Int {
    case Grain, Potato, Sugars, Pulses, SeedsAndNuts, Vegetables, Mushrooms, Fruits, Meat, Eggs, Seafoods, Seaweed, MilkProducts, FatAndOils, Drinks, Seasoning, Etc
}


 이제 TableViewController에 데이터를 추가해 보자. 먼저 구현한 코드를 보자.


RefrigeratorTableViewController.swift

import UIKit

class RefrigeratorTableViewController : UITableViewController {
    var list = Array<FoodVO>()
    var dateFormat = DateFormatter()
    
    override func viewDidLoad() {
        dateFormat.dateFormat = "yyyy-MM-dd"
        dateFormat.locale = Locale.init(identifier: "ko_KR")
        
        var fvo = FoodVO()
        fvo.name = "서울우유"
        fvo.category = FoodCategory.MilkProducts
        fvo.sellByDate = dateFormat.date(from: "2017-04-20")
        list.append(fvo)
        
        fvo = FoodVO()
        fvo.name = "3분짜장"
        fvo.category = FoodCategory.Etc
        fvo.sellByDate = dateFormat.date(from: "2017-07-31")
        list.append(fvo)
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return list.count
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let row = list[indexPath.row]
        let cell = tableView.dequeueReusableCell(withIdentifier: "SokoCell")!
        cell.textLabel?.text = row.name
        cell.detailTextLabel?.text = "유통기한: \(dateFormat.string(from: row.sellByDate!))"
        
        return cell
    }
    
    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        NSLog("Touch Table Row at %d", indexPath.row)
    }
}


 viewDidLoad() 메서드는 해당 ViewController가 화면에 표시될 때 가장 먼저 실행되는 메서드다. 따라서 override하여 정의해야 내가 원하는 기능을 추가 할 수 있다. 위의 코드에서는 dateFormat을 초기화 하고, 임의의 데이터를 직접 list에 추가하는 코드를 작성하였다. 실제 앱을 작성할 때에는 여기에 직접 데이터를 입력하지 않고 iOS 내부의 데이터베이스에서 데이터를 가져오거나, 웹을 통해 호출해 저장해야 할 것이다.

 tableView(_ tableView:, numberOfRowsInSection section: Int) -> Int 메서드는 몇 개의 줄이 표시되어야 하는지 반환하는 함수이다. 앱이 이 메서드를 호출하여 몇 개의 줄이 필요한지 확인할 것이다. 현재 코드에서는 list라는 배열의 크기만큼 리스트를 표시할 것이다.

 tableView(_ tableView:, cellForRowAt indexPath:) -> UITableViewCell 메서드는 Cell을 반환한다. 즉 여기서 Cell을 생성하고, Cell안에 표시할 내용을 추가하고 앱에 반환하는 것이다. 여기서는 list에서 VO를 불러와 각 내용을 textLabel과 detailTextLabel에 추가하였다.

 tableView(_ tableView:, didSelecRowAt indexPath:) 메서드는 Cell을 클릭했을 때 어떠한 행동을 할 것인지 정의하는 메서드이다.


 이제 다시 실행 해보자.


 이제 '+'버튼을 누르면 '재료 추가' 뷰로 이동하도록 바꿔보자. 먼저 새로운 ViewController를 만들고 아래와 같이 구성한다.


 그리고 기존 테이블 뷰 컨트롤러의 +버튼을 ctrl 버튼을 누른채로 드래그한 후 새로 만든 ViewController에 드롭하면 아래와 같은 창이 뜬다. [Present Modally]를 선택한다. 그럼 두 ViewController가 연결되는 것을 볼 수 있다. 앱을 실행하면 +버튼을 누르면 새로 생성한 ViewController로 넘어가게 된다.




 앱을 실행하면 '+' 버튼을 눌렀을때 오른쪽 View Controller는 표시 되지만, 다시 돌아오는건 작동하지 않는다. unwind 기능을 추가하여야 한다. unwind 메서드는 돌아가고자 하는 컨트롤러에서 작성해야 한다.


RefrigeratorTableViewController.swift

import UIKit

class RefrigeratorTableViewController : UITableViewController {
    
    (... 생략 ...)
    
    @IBAction func unwindToMainViewController(segue : UIStoryboardSegue) {
        
    }
    
    @IBAction func unwindToMainViewControllerAndRefreshList(seque : UIStoryboardSegue) {
        
    }

}


 unwindToMainViewController는 '닫기' 버튼을 눌렀을때 실행될 메서드이고 unwindToMainViewControllerAndRefreshList는 '추가' 버튼을 눌렀을 때 리스트를 새로고침 하기 위한 메서드이다.

 이제 스토리보드에서 '닫기' 버튼을 ctrl을 누른채 드래그하여 ViewController 상단의 세번재 아이콘인 [Exit]에 드롭한다. 그리고 unwindToMainViewController에 연결한다. 마찬가지로 '추가' 버튼도 연결한다.

 


이제 앱을 실행하면 추가 페이지가 열리고 닫히는 모습을 볼 수 있다.



 다음에는 데이터베이스에 이 재료 object들을 저장할 것이다. SQLite를 사용하려 계획하였으나 최근 Realm이라는 데이터베이스가 각광받는다기에 학습하며 적용해 보려 한다. 현재 위의 코드에서 재료 추가 뷰에서 값을 입력받아 데이터베이스에 저장하고, 데이터베이스에서 다시 값을 읽어와 리스트에 출력할 것이다.

 왜 iOS 앱을 개발하려 하는지 특별한 이유는 없다. 그저 내가 아이폰을 쓰고 있기에, 이걸로 실제로 사용하는 앱을 만들어 보고자 할 뿐이다. Objective-C의 문법은 다른 객체지향 언어와 비교하여 생소한 문법을 많이 가지고 있었기에 접근하기 까다로웠지만, Swift는 좀더 세련되고 보편적인 형태의 문법을 가지고 있기에 배우기 쉽다 생각하여 선택하게 되었다.


책은 <꼼꼼한 재은 씨의 스위프트2 프로그래밍>을 도서관에서 대여하여 참고하였다. 현재는 Swift3 버전까지 공개되어 있지만 약간의 변경만 있을뿐 기초적인 부분에서는 큰 변화가 없기 때문에 이 책으로 개발하는데 큰 무리가 없을 것으로 보인다. 참고로 저 책도 현재 Swift3로 판올림 되어 판매되고 있다. 이 책은 굉장히 두껍지만(페이지만 1144쪽) 프로그래밍 초보도 이해할 수 있을 정도로 쉽게 설명되어있기 때문에 초보자에게 큰 도움이 될 것이라 생각하며, 나와 같이 약간의 프로그래밍 지식이 있는 사람은 Swift 문법 부분을 금방 훑고 지나갈 수 있으리라 생각한다.


 개발하려는 앱은 단순하게 말해서 '냉장고 앱'이다. 어머니가 워낙 손이 커서 매번 많은 양의 장을 보는데, 유통기한이 지나 버리게 되는 음식물들을 보면서 아깝다는 생각이 들어 이를 체계적으로 관리하는 앱이 있으면 어떨까 하는 생각에 개발을 생각하게 되었다. App Store에도 비슷한 앱이 몇 개 존재하긴 하나 현재 업데이트가 멈춰있고, 내가 필요로 하는 기능들이 부족한 듯 하여 부족한 부분이 개선된 앱을 만들어 볼 것이다. (계획은 꽤 오래전에 했는데 카카오 면접에 떨어지고, 하릴없이 놀고만 있을수 없어서 실천에 옮기게 되었다.)


 이러한 단순한 아이디어를 구현하는 앱에서 출발하여 끝에는 Ruby on Rails로 구현한 웹 서버와 연동되는 앱을 만들것이다. 아직 구체적 방향은 나오지 않았지만, 대략적으로 어떻게 개발했으면 좋을까 하는 감은 가지고 있다. 아래는 대강 이 앱의 구성을 낙서하듯 그려본 것이다.







 매일 조금씩이라도 개발하여 앱 스토어에 등록할 수 있도록 노력해 볼 것이다.



앱 이름은 '소코(Soko)'로 정했다. 일본어로 창고라는 뜻이라한다. 아래처럼 앱을 생성했다.



 간단한 레이아웃만 잡아준다. 아래처럼 기본 View Controller를 삭제하고 우측하단 'Show the Object library'에서 Tab Bar Controller를 화면에 끌어 놓는다. 



 Tab Bar Controller가 시작화면이 될 수 있게 우측 상단의 'Show the Attributes Inspector'에서 'Is Initial View Controller'에 체크표시한다.



탭 바에 화면이 4개가 필요하기 때문에 View Controller를 추가로 생성해서 끌어놓는다. 그리고 Tab Bar Controller의 회색면을 Ctrl을 누른채로 클릭하고 새로 추가한 View Controller에 놓는다. 그리고 'Relationship Segue' 밑의 'view controllers'를 클릭한다.



 각 View Controller에 이름을 설정해 주었다.






 각 View Controller 밑에 있는 Tab Bar Item을 클릭하고 Title을 설정한 뒤 Custom 아이콘 이미지를 넣어준다. 나는 아래의 사이트에서 다운받아 사용 하였다.

http://www.iconbeast.com/free/




 각 View Controller 상단에 구별할 수 있는 Label을 추가하여 집어넣고 실행하여 확인해 보았다.



 코드는 https://github.com/Stardust-kr/soko 에 지속접으로 업데이트 될 것이다.


 자, 이제 시작이다.

+ Recent posts