//

//  TableViewController.swift

//  Table

//

//  Created by stayfoolish on 19/10/2018.

//  Copyright © 2018 stayfoolish. All rights reserved.

//


import UIKit


// 앱 시작 시 기본적으로 나타낼 목록

var items = ["책 구매","철수와 약속","스터디 준비하기"]

var itemsImageFile = ["cart.png","clock.png","pencil.png"]


class TableViewController: UITableViewController {


    @IBOutlet var tvListView: UITableView!

    

    override func viewDidLoad() {

        super.viewDidLoad()


        // Uncomment the following line to preserve selection between presentations

        // self.clearsSelectionOnViewWillAppear = false


        // Uncomment the following line to display an Edit button in the navigation bar for this view controller.

         self.navigationItem.leftBarButtonItem = self.editButtonItem

    }

    

    // 뷰가 노출될 때마다 리스트의 데이터를 다시 불러옴

    override func viewWillAppear(_ animated: Bool) {

        tvListView.reloadData()

    }

    

    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

        // Dispose of any resources that can be recreated.

    }


    // MARK: - Table view data source

    // 테이블 안의 섹션 개수를 1로 설정함

    override func numberOfSections(in tableView: UITableView) -> Int {

        // #warning Incomplete implementation, return the number of sections

        return 1

    }


    // 섹션당 열의 개수를 전달

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        // #warning Incomplete implementation, return the number of rows

        return items.count

    }


    // items와 itemsImageFile의 값을 셀에 삽입함

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath)


        // Configure the cell...

        cell.textLabel?.text = items[(indexPath as NSIndexPath).row]

        cell.imageView?.image = UIImage(named: itemsImageFile[(indexPath as NSIndexPath).row])

        

        return cell

    }

    


    /*

    // Override to support conditional editing of the table view.

    override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {

        // Return false if you do not want the specified item to be editable.

        return true

    }

    */


    

    // Override to support editing the table view.

    // 목록 삭제 함수

    override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {

        if editingStyle == .delete {

            // Delete the row from the data source

            // items와 itemsImageFile에서 해당 리스트를 삭제함

            items.remove(at: (indexPath as NSIndexPath).row)

            itemsImageFile.remove(at: (indexPath as NSIndexPath).row)

            tableView.deleteRows(at: [indexPath], with: .fade)

        } else if editingStyle == .insert {

            // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view

        }    

    }

    

    // 삭제 시 "Delete" 대신 "삭제"로 표시

    override func tableView(_ tableView: UITableView, titleForDeleteConfirmationButtonForRowAt indexPath: IndexPath) -> String? {

        return "삭제"

    }

    

    

    // Override to support rearranging the table view.

    // 목록 순서 바꾸기

    override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {

        

        let itemToMove = items[(fromIndexPath as NSIndexPath).row]

        let itemImageToMove = itemsImageFile[(fromIndexPath as NSIndexPath).row]

        items.remove(at: (fromIndexPath as NSIndexPath).row)

        itemsImageFile.remove(at: (fromIndexPath as NSIndexPath).row)

        items.insert(itemToMove, at: (to as NSIndexPath).row)

        itemsImageFile.insert(itemImageToMove, at: (to as NSIndexPath).row)

    }

    


    /*

    // Override to support conditional rearranging of the table view.

    override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {

        // Return false if you do not want the item to be re-orderable.

        return true

    }

    */


    

    // MARK: - Navigation


    // 세그웨이를 이용하여 디테일 뷰로 전환하기

    // In a storyboard-based application, you will often want to do a little preparation before navigation

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

        // Get the new view controller using segue.destinationViewController.

        // Pass the selected object to the new view controller.

        if segue.identifier == "sgDetail" {

            let cell = sender as! UITableViewCell

            let indexPath = self.tvListView.indexPath(for: cell)

            let detailView = segue.destination as! DetailViewController

            detailView.receiveItem(items[((indexPath as NSIndexPath?)?.row)!])

        }

    }

    


}


//

//  AddViewController.swift

//  Table

//

//  Created by stayfoolish on 19/10/2018.

//  Copyright © 2018 stayfoolish. All rights reserved.

//


import UIKit


class AddViewController: UIViewController {


    @IBOutlet var tfAddItem: UITextField!

    override func viewDidLoad() {

        super.viewDidLoad()


        // Do any additional setup after loading the view.

    }


    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

        // Dispose of any resources that can be recreated.

    }

    


    /*

    // MARK: - Navigation


    // In a storyboard-based application, you will often want to do a little preparation before navigation

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

        // Get the new view controller using segue.destinationViewController.

        // Pass the selected object to the new view controller.

    }

    */

    // 새 목록 추가하기

    @IBAction func btnAddItem(_ sender: UIButton) {

        items.append(tfAddItem.text!)

        itemsImageFile.append("clock.png")

        tfAddItem.text=""

        _ = navigationController?.popViewController(animated: true)

    }

    

}


//

//  DetailViewController.swift

//  Table

//

//  Created by stayfoolish on 19/10/2018.

//  Copyright © 2018 stayfoolish. All rights reserved.

//


import UIKit


class DetailViewController: UIViewController {

    

    var receiveItem = ""


    @IBOutlet var lblItem: UILabel!

    

    override func viewDidLoad() {

        super.viewDidLoad()


        // Do any additional setup after loading the view.

        lblItem.text = receiveItem

    }


    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

        // Dispose of any resources that can be recreated.

    }

    

    // Main View에서 변수를 받아오기 위한 함수

    func receiveItem(_ item: String){

        receiveItem = item

    }

    


    /*

    // MARK: - Navigation


    // In a storyboard-based application, you will often want to do a little preparation before navigation

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

        // Get the new view controller using segue.destinationViewController.

        // Pass the selected object to the new view controller.

    }

    */


}












10장 탭 바 컨트롤러 이용해 여러 개의 뷰 넣기




//

//  ViewController.swift

//  Tab

//

//  Created by stayfoolish on 18/10/2018.

//  Copyright © 2018 stayfoolish. All rights reserved.

//


import UIKit


class ViewController: UIViewController {


    override func viewDidLoad() {

        super.viewDidLoad()

        // Do any additional setup after loading the view, typically from a nib.

    }


    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

        // Dispose of any resources that can be recreated.

    }


    @IBAction func btnMoveImageView(_ sender: UIButton) {

        tabBarController?.selectedIndex = 1

    }

    

    @IBAction func btnMoveDatePickerView(_ sender: UIButton) {

        tabBarController?.selectedIndex = 2

    }

    

}



//

//  ViewController.swift

//  ImageView

//

//  Created by stayfoolish on 16/10/2018.

//  Copyright © 2018 stayfoolish. All rights reserved.

//


import UIKit


class ImageViewController: UIViewController {

    

    var isZoom = false

    var imgOn: UIImage?

    var imgOff: UIImage?


    @IBOutlet var imgView: UIImageView!

    

    @IBOutlet var btnResize: UIButton!

    override func viewDidLoad() {

        super.viewDidLoad()

        // Do any additional setup after loading the view, typically from a nib.

        imgOn = UIImage(named: "lamp_on.png")

        imgOff = UIImage(named: "lamp_off.png")

        

        imgView.image = imgOn

    }


    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

        // Dispose of any resources that can be recreated.

    

    }


    @IBAction func btnResizeImage(_ sender: UIButton) {

        let scale:CGFloat = 2.0

        var newWidth:CGFloat, newHeight:CGFloat

        

        if (isZoom) { // true

            newWidth = imgView.frame.width/scale

            newHeight = imgView.frame.height/scale

            imgView.frame.size = CGSize(width: newWidth, height: newHeight)

            btnResize.setTitle("확대", for: .normal)

        }

        else { // flase

            newWidth = imgView.frame.width*scale

            newHeight = imgView.frame.height*scale

            imgView.frame.size = CGSize(width: newWidth, height: newHeight)

            btnResize.setTitle("축소", for: .normal)

        }

        isZoom = !isZoom

    }

    

    @IBAction func switchImageOnOff(_ sender: UISwitch) {

        if sender.isOn {

            imgView.image = imgOn

        }else {

            imgView.image = imgOff

        }

    }

}



//

//  ViewController.swift

//  DatePicker

//

//  Created by stayfoolish on 16/10/2018.

//  Copyright © 2018 stayfoolish. All rights reserved.

//


import UIKit


class DateViewController: UIViewController {

    let timeSelector: Selector = #selector(DateViewController.updateTime)

    let interval = 1.0

    var count = 0

    @IBOutlet var lblCurrentTime: UILabel!

    @IBOutlet var lblPickerTime: UILabel!

    

    override func viewDidLoad() {

        super.viewDidLoad()

        // Do any additional setup after loading the view, typically from a nib.

        

        Timer.scheduledTimer(timeInterval: interval, target: self, selector: timeSelector, userInfo: nil, repeats: true)

    }


    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

        // Dispose of any resources that can be recreated.

    }


    @IBAction func changeDatePicker(_ sender: UIDatePicker) {

        let datePickerview = sender

        

        let formatter = DateFormatter()

        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss EEE"

        lblPickerTime.text = "선택시간: " + formatter.string(from: datePickerview.date)

    }

    

    @objc func updateTime() {

//        lblCurrentTime.text = String(count)

//        count = count + 1

        

        let date = NSDate()

        

        let formatter = DateFormatter()

        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss EEE"

        lblCurrentTime.text = "현재시간: " + formatter.string(from: date as Date)

    }

    @IBAction func btnMoveFirst(_ sender: UIButton) {

        tabBarController?.selectedIndex = 0

    }

}




11장 내비게이션 컨트롤러 이용해 화면 전환하기




//

//  ViewController.swift

//  Navigation

//

//  Created by stayfoolish on 18/10/2018.

//  Copyright © 2018 stayfoolish. All rights reserved.

//


import UIKit


class ViewController: UIViewController,EditDelegate {


    let imgOn = UIImage(named: "lamp_on.png")

    let imgOff = UIImage(named: "lamp_off.png")

    

    var isOn = true

    


    @IBOutlet var txMessage: UITextField!

    @IBOutlet var imgView: UIImageView!

    

    override func viewDidLoad() {

        super.viewDidLoad()

        // Do any additional setup after loading the view, typically from a nib.

        imgView.image = imgOn

    }


    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

        // Dispose of any resources that can be recreated.

    }


    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

        let editViewController = segue.destination as! EditViewController

        if segue.identifier == "editButton" {

            editViewController.textWayValue = "segue : use button"

        }else if segue.identifier == "editBarButton" {

            editViewController.textWayValue = "segue: use Bar button"

        }

        editViewController.textMessage = txMessage.text!

        editViewController.isOn = isOn

        editViewController.delegate = self

    }

    

    func didMessageEditDone(_ controller: EditViewController, message: String) {

        txMessage.text = message

    }

    

    func didImageOnOffDone(_ controller: EditViewController, isOn: Bool){

        if isOn {

            imgView.image = imgOn

            self.isOn = true

        }else {

            imgView.image = imgOff

            self.isOn = false 

        }

    }

    


}



//

//  EditViewController.swift

//  Navigation

//

//  Created by stayfoolish on 18/10/2018.

//  Copyright © 2018 stayfoolish. All rights reserved.

//


import UIKit


protocol EditDelegate {

    func didMessageEditDone(_ controller: EditViewController, message: String)

    func didImageOnOffDone(_ controller: EditViewController, isOn: Bool)

    

}


class EditViewController: UIViewController {


    

    

    var textWayValue: String = ""

    var textMessage: String = ""

    var delegate: EditDelegate?

    var isOn = false


    @IBOutlet var lblWay: UILabel!

    @IBOutlet var txMessage: UITextField!

    @IBOutlet var swIson: UISwitch!

    

    override func viewDidLoad() {

        super.viewDidLoad()


        // Do any additional setup after loading the view.

        lblWay.text = textWayValue

        txMessage.text = textMessage

        swIson.isOn = isOn

    }


    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

        // Dispose of any resources that can be recreated.

    }

    

    @IBAction func btnDone(_ sender: UIButton) {

        if delegate != nil {

            delegate?.didMessageEditDone(self, message: txMessage.text!)

            delegate?.didImageOnOffDone(self, isOn: isOn)

        }

        _ = navigationController?.popViewController(animated: true)

    }

    

    @IBAction func swImageOnOff(_ sender: UISwitch) {

        if sender.isOn {

            isOn = true

        }else {

            isOn = false

        }

    }

    /*

    // MARK: - Navigation


    // In a storyboard-based application, you will often want to do a little preparation before navigation

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

        // Get the new view controller using segue.destinationViewController.

        // Pass the selected object to the new view controller.

    }

    */


}











07장 웹뷰 WebView




//

//  ViewController.swift

//  Web

//

//  Created by stayfoolish on 17/10/2018.

//  Copyright © 2018 stayfoolish. All rights reserved.

//


import UIKit


class ViewController: UIViewController,UIWebViewDelegate {

    @IBOutlet var txtUrl: UITextField!

    @IBOutlet var myWebView: UIWebView!

    @IBOutlet var myActivityIndicator: UIActivityIndicatorView!

    

    func loadWebPage(_ url: String){

        let myUrl = URL(string: url)

        let myRequest = URLRequest(url: myUrl!)

        myWebView.loadRequest(myRequest)

    }

    

    override func viewDidLoad() {

        super.viewDidLoad()

        // Do any additional setup after loading the view, typically from a nib.

        myWebView.delegate = self

        loadWebPage("https://developer.apple.com/kr/swift")

        

    }


    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

        // Dispose of any resources that can be recreated.

    }

    

    func webViewDidStartLoad(_ webView: UIWebView) {

        myActivityIndicator.startAnimating()

    }

    

    func webViewDidFinishLoad(_ webView: UIWebView) {

        myActivityIndicator.stopAnimating()

    }

    func checkUrl(_ url: String) -> String {

        var strUrl = url

        let flag = strUrl.hasPrefix("http://")

        if !flag {

            strUrl = "http://" + strUrl

        }

        return strUrl

    }


    @IBAction func btnGotoUrl(_ sender: UIButton){

        let myUrl = checkUrl(txtUrl.text!)

        txtUrl.text = ""

        loadWebPage(myUrl)

    }

    @IBAction func btnGoSite1(_ sender: UIButton){

//        loadWebPage("http://fallinmac.tistory.com")

        loadWebPage("http://www.gurubee.net/")

        

    }

    @IBAction func btnGoSite2(_ sender: UIButton){

        loadWebPage("http://www.easyspub.co.kr/")

    }

    @IBAction func btnLoadHtmlString(_ sender: UIButton){

        let htmlString = "<h1> HTML String </h1><p> String 변수를 이용한 웹페이지 </p> <p><a href=\"https://developer.apple.com/kr/swift\">swift</a>로 이동</p>"

        myWebView.loadHTMLString(htmlString, baseURL: nil)

    }

    @IBAction func btnLoadHtmlFile(_ sender: UIButton){

        let myHtmlBundle = Bundle.main

        let filePath = myHtmlBundle.path(forResource: "htmlView", ofType: "html")

        loadWebPage(filePath!)

    }

    @IBAction func btnStop(_ sender: UIBarButtonItem){

        myWebView.stopLoading()

    }

    @IBAction func btnReload(_ sender: UIBarButtonItem){

        myWebView.reload()

    }

    @IBAction func btnGoBack(_ sender: UIBarButtonItem){

        myWebView.goBack()

    }

    @IBAction func btnGoForward(_ sender: UIBarButtonItem){

        myWebView.goForward()

    }

}




8장 MapView 맵뷰




//

//  ViewController.swift

//  Map

//

//  Created by stayfoolish on 17/10/2018.

//  Copyright © 2018 stayfoolish. All rights reserved.

//


import UIKit

import MapKit




class ViewController: UIViewController, CLLocationManagerDelegate {


    @IBOutlet var myMap: MKMapView!

    @IBOutlet var lblLocationInfo1: UILabel!

    @IBOutlet var lblLocationInfo2: UILabel!

    

    let locationManager = CLLocationManager()

    

    override func viewDidLoad() {

        super.viewDidLoad()

        // Do any additional setup after loading the view, typically from a nib.

        lblLocationInfo1.text = ""

        lblLocationInfo2.text = ""

        locationManager.delegate = self

        locationManager.desiredAccuracy = kCLLocationAccuracyBest

        locationManager.requestWhenInUseAuthorization()

        locationManager.startUpdatingLocation()

        myMap.showsUserLocation = true

        

    }


    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

        // Dispose of any resources that can be recreated.

    }

    

    func goLocation(latitudeValue: CLLocationDegrees, longitudeValue: CLLocationDegrees, delta span : Double) -> CLLocationCoordinate2D{

        let pLocation = CLLocationCoordinate2DMake(latitudeValue, longitudeValue)

        let spanValue = MKCoordinateSpanMake(span, span)

        let pRegion = MKCoordinateRegionMake(pLocation, spanValue)

        myMap.setRegion(pRegion, animated: true)

        return pLocation

    }

    

    func setAnnotation(latitudeValue: CLLocationDegrees, longitudeValue : CLLocationDegrees, delta span : Double, title strTitle : String, subtitle strSubtitle: String){

        let annotation = MKPointAnnotation()

        annotation.coordinate = goLocation(latitudeValue: latitudeValue, longitudeValue: longitudeValue, delta: span)

        annotation.title = strTitle

        annotation.subtitle = strSubtitle

        myMap.addAnnotation(annotation)

    }

    


    

    

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]){

        let pLocation = locations.last

         _ = goLocation(latitudeValue: (pLocation?.coordinate.latitude)!, longitudeValue: (pLocation?.coordinate.longitude)!, delta: 0.01)

        CLGeocoder().reverseGeocodeLocation(pLocation!, completionHandler: {

            (placemarks, error) -> Void in

            let pm = placemarks!.first

            let country = pm!.country

            var address:String = country!

            if pm!.locality != nil {

                address += " "

                address += pm!.locality!

            }

            if pm!.thoroughfare != nil {

                address += " "

                address += pm!.thoroughfare!

            }

            self.lblLocationInfo1.text = "현재 위치"

            self.lblLocationInfo2.text = address

        })

        locationManager.stopUpdatingLocation()

    }

 

    


    @IBAction func sgChangeLocation(_ sender: UISegmentedControl) {

        

        if sender.selectedSegmentIndex == 0 {

            self.lblLocationInfo1.text = ""

            self.lblLocationInfo2.text = ""

            locationManager.startUpdatingLocation()

        }else if sender.selectedSegmentIndex == 1 {

            setAnnotation(latitudeValue: 37.751853, longitudeValue: 128.87605740000004, delta: 1, title: "한국폴리텍대학 강릉캠퍼스", subtitle: "강원도 강릉시 남산초교길 121")

            self.lblLocationInfo1.text = "보고 계신 위치"

            self.lblLocationInfo2.text = "한국폴리텍대학 강릉캠퍼스"

        }else if sender.selectedSegmentIndex == 2 {

            setAnnotation(latitudeValue: 37.5307871, longitudeValue: 126.8981, delta: 0.1, title: "이지스퍼블리싱", subtitle: "서울시 영등포구 당산로 41길 11")

            self.lblLocationInfo1.text = "보고 계신 위치"

            self.lblLocationInfo2.text = "이지스퍼블리싱 출판사 "

        }

    }


}




09장 PageControl 페이지 컨트롤




//

//  ViewController.swift

//  PageControl

//

//  Created by stayfoolish on 17/10/2018.

//  Copyright © 2018 stayfoolish. All rights reserved.

//


import UIKit


var images = ["01.png","02.png","03.png","04.png","05.png","06.png"]


class ViewController: UIViewController {

    @IBOutlet var imgView: UIImageView!

    @IBOutlet var pageControl: UIPageControl!

    

    

    override func viewDidLoad() {

        super.viewDidLoad()

        // Do any additional setup after loading the view, typically from a nib.

        pageControl.numberOfPages = images.count

        pageControl.currentPage = 0

        pageControl.pageIndicatorTintColor = UIColor.green

        pageControl.currentPageIndicatorTintColor = UIColor.red

        imgView.image = UIImage(named: images[0])

        

    }


    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

        // Dispose of any resources that can be recreated.

    }


    @IBAction func pageChanged(_ sender: UIPageControl) {

        imgView.image = UIImage(named: images[pageControl.currentPage])

    }

    

}

















04장  데이트 피커 사용해 날짜 선택하기



//

//  ViewController.swift

//  DatePicker

//

//  Created by stayfoolish on 16/10/2018.

//  Copyright © 2018 stayfoolish. All rights reserved.

//


import UIKit


class ViewController: UIViewController {

    let timeSelector: Selector = #selector(ViewController.updateTime)

    let interval = 1.0

    var count = 0

    @IBOutlet var lblCurrentTime: UILabel!

    @IBOutlet var lblPickerTime: UILabel!

    

    override func viewDidLoad() {

        super.viewDidLoad()

        // Do any additional setup after loading the view, typically from a nib.

        

        Timer.scheduledTimer(timeInterval: interval, target: self, selector: timeSelector, userInfo: nil, repeats: true)

    }


    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

        // Dispose of any resources that can be recreated.

    }


    @IBAction func changeDatePicker(_ sender: UIDatePicker) {

        let datePickerview = sender

        

        let formatter = DateFormatter()

        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss EEE"

        lblPickerTime.text = "선택시간: " + formatter.string(from: datePickerview.date)

    }

    

    @objc func updateTime() {

//        lblCurrentTime.text = String(count)

//        count = count + 1

        

        let date = NSDate()

        

        let formatter = DateFormatter()

        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss EEE"

        lblCurrentTime.text = "현재시간: " + formatter.string(from: date as Date)

    }

}




05장  피커 뷰 사용해 원하는 항목 선택하기



//

//  ViewController.swift

//  PickerView

//

//  Created by stayfoolish on 17/10/2018.

//  Copyright © 2018 stayfoolish. All rights reserved.

//


import UIKit


class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {

    

    let MAX_ARRAY_NUM = 10

    let PICKER_VIEW_COLUMN = 1

    let PICKER_VIEW_HEIGHT:CGFloat = 80

    var imageArray = [UIImage?]()

    var imageFileName = ["1.jpg","2.jpg","3.jpg","4.jpg","5.jpg","6.jpg","7.jpg","8.jpg","9.jpg","10.jpg"]


    @IBOutlet var pickerImage: UIPickerView!

    @IBOutlet var lblImageFileName: UILabel!

    @IBOutlet var imageView: UIImageView!

    

    override func viewDidLoad() {

        super.viewDidLoad()

        // Do any additional setup after loading the view, typically from a nib.

        

        for i in 0..<MAX_ARRAY_NUM {

            let image = UIImage(named: imageFileName[i])

            imageArray.append(image)

        }

        

        lblImageFileName.text = imageFileName[0]

        imageView.image = imageArray[0]

    }


    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

        // Dispose of any resources that can be recreated.

    }


    func numberOfComponents(in pickerView: UIPickerView) -> Int {

        return PICKER_VIEW_COLUMN

    }

    

    func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat {

        return PICKER_VIEW_HEIGHT

    }


    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {

        return imageFileName.count

    }

    

//    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {

//        return imageFileName[row]

//    }


    func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {

        let imageView = UIImageView(image: imageArray[row])

        imageView.frame = CGRect(x: 0, y: 0, width: 100, height: 150)

        

        return imageView

    }

    

    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {

        lblImageFileName.text = imageFileName[row]

        imageView.image = imageArray[row]

    }

}




06장 얼럿 사용해 경고 표시하기 



//

//  ViewController.swift

//  Alert

//

//  Created by stayfoolish on 17/10/2018.

//  Copyright © 2018 stayfoolish. All rights reserved.

//


import UIKit


class ViewController: UIViewController {

    

    let imgOn = UIImage(named: "lamp-on.png")

    let imgOff = UIImage(named: "lamp-off.png")

    let imgRemove = UIImage(named: "lamp-remove.png")

    

    var isLampOn = true


    @IBOutlet var lampImg: UIImageView!

    override func viewDidLoad() {

        super.viewDidLoad()

        // Do any additional setup after loading the view, typically from a nib.

        

        lampImg.image = imgOn

    }


    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

        // Dispose of any resources that can be recreated.

    }


    @IBAction func btnLampOn(_ sender: UIButton) {

        if(isLampOn==true){

            let lampOnAlert = UIAlertController(title: "경고", message: "현재 On 상태입니다.", preferredStyle: UIAlertControllerStyle.alert)

            let onAction = UIAlertAction(title: "네, 알겠습니다.", style: UIAlertActionStyle.default, handler: nil )

            lampOnAlert.addAction(onAction)

            present(lampOnAlert, animated: true, completion: nil)

        }else{

            lampImg.image = imgOn

            isLampOn=true

        }

    }

    

    @IBAction func btnLampOff(_ sender: UIButton) {

        if isLampOn {

            let lampOffAlert = UIAlertController(title: "램프 끄기", message: "램프를 끄시겠습니까?", preferredStyle: UIAlertControllerStyle.alert)

            let offAction = UIAlertAction(title: "네", style: UIAlertActionStyle.default, handler: {ACTION in self.lampImg.image = self.imgOff

                self.isLampOn=false

            })

            let cancelAction = UIAlertAction(title: "아니오", style: UIAlertActionStyle.default, handler: nil )

            

            lampOffAlert.addAction(offAction)

            lampOffAlert.addAction(cancelAction)

            

            present(lampOffAlert, animated: true, completion: nil )

        }

    }

    

    @IBAction func btnLampRemove(_ sender: UIButton) {

        let lampRemoveAlert = UIAlertController(title: "램프 제거", message: "램프를 제거하시겠습니까?", preferredStyle: UIAlertControllerStyle.alert)

        

        let offAction = UIAlertAction(title: "아니오, 끕니다(off).", style: UIAlertActionStyle.default, handler: {ACTION in self.lampImg.image = self.imgOff

            self.isLampOn=false

        })

        

        let onAction = UIAlertAction(title: "아니오, 켭니다(on).", style: UIAlertActionStyle.default){

        ACTION in self.lampImg.image = self.imgOn

        self.isLampOn=true

        }

        let removeAction = UIAlertAction(title: "네, 제거합니다.", style: UIAlertActionStyle.destructive, handler: {

            ACTION in self.lampImg.image = self.imgRemove

            self.isLampOn=false

        })

        

        lampRemoveAlert.addAction(offAction)

        lampRemoveAlert.addAction(onAction)

        lampRemoveAlert.addAction(removeAction)

        present(lampRemoveAlert, animated: true, completion: nil )

    }

}























01. 아이폰 앱 개발 준비하기


* 아이폰 시장

2016년 4분기 세계 스마트폰 점유율 : 애플 20.3 %

출처 : 트렌드포스(2017년 4월)


* 스위프트의 장점

- 빠르고 강력하다

- 완전한 플랫폼이다.

- 현대적이다.

- 상호 반응적인 플레이그라운드

- 안전을 위한 설계

- 오브젝티브-c와의 상호 운용성

- 오픈 소스이다.

- 스위프트 특징 : https://developer.apple.com/swift

- 스위프트 언어 개발 문서 : http://swift.leantra.kr ( 현재시점 내용이 보이지 않음)

- 직접 알게 된 스위프트 한글 번역 사이트 :  https://jusung.gitbook.io/the-swift-language-guide/


*Xcode가 설치된 맥 PC 준비하기

 Xcode가 macOs에서만 동작한다.


02. Hello World 앱 만들며 Xcode에 완벽 적응하기




//

//  ViewController.swift

//  HelloWorld

//

//  Created by stayfoolish on 16/10/2018.

//  Copyright © 2018 stayfoolish. All rights reserved.

//


import UIKit


class ViewController: UIViewController {


    @IBOutlet var lblHello: UILabel!

    @IBOutlet var txtName: UITextField!

    

    override func viewDidLoad() {

        super.viewDidLoad()

        // Do any additional setup after loading the view, typically from a nib.

    }


    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

        // Dispose of any resources that can be recreated.

    }


    @IBAction func btnSend(_ sender: UIButton) {

        lblHello.text = "Hello, " + txtName.text!

    }

    

}







03. 원하는 이미지 화면에 출력하기 - 이미지뷰





//

//  ViewController.swift

//  ImageView

//

//  Created by stayfoolish on 16/10/2018.

//  Copyright © 2018 stayfoolish. All rights reserved.

//


import UIKit


class ViewController: UIViewController {

    

    var isZoom = false

    var imgOn: UIImage?

    var imgOff: UIImage?


    @IBOutlet var imgView: UIImageView!

    

    @IBOutlet var btnResize: UIButton!

    override func viewDidLoad() {

        super.viewDidLoad()

        // Do any additional setup after loading the view, typically from a nib.

        imgOn = UIImage(named: "lamp_on.png")

        imgOff = UIImage(named: "lamp_off.png")

        

        imgView.image = imgOn

    }


    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

        // Dispose of any resources that can be recreated.

    

    }


    @IBAction func btnResizeImage(_ sender: UIButton) {

        let scale:CGFloat = 2.0

        var newWidth:CGFloat, newHeight:CGFloat

        

        if (isZoom) { // true

            newWidth = imgView.frame.width/scale

            newHeight = imgView.frame.height/scale

            imgView.frame.size = CGSize(width: newWidth, height: newHeight)

            btnResize.setTitle("확대", for: .normal)

        }

        else { // flase

            newWidth = imgView.frame.width*scale

            newHeight = imgView.frame.height*scale

            imgView.frame.size = CGSize(width: newWidth, height: newHeight)

            btnResize.setTitle("축소", for: .normal)

        }

        isZoom = !isZoom

    }

    

    @IBAction func switchImageOnOff(_ sender: UISwitch) {

        if sender.isOn {

            imgView.image = imgOn

        }else {

            imgView.image = imgOff

        }

    }

}









//Object Oriented Programming 객체지향 프로그래밍

//Class와 Structure



struct Resolution {

    var width = 5

    var height = 10

}


var resolution1 = Resolution()

resolution1.height

resolution1.width


var resolution2 = Resolution()

resolution2.height = 30

resolution2.width = 15


class VideoMode {

    var resolution = Resolution()

    var frameRate = 0.0

    var name = ""

}


let videoMode = VideoMode()

videoMode.frameRate

videoMode.name

videoMode.resolution.height



// Value Type vs Reference Type

// struct     vs class


let hd = Resolution(width: 1920, height: 1080)

var cinema = hd

cinema.width = 2048

hd.width // 1920

cinema.width // 2048


let tenEighty = VideoMode()

tenEighty.resolution = hd

tenEighty.name = "1080i"

tenEighty.frameRate = 25.0


let anotherTenEighty = tenEighty

anotherTenEighty.frameRate = 30

tenEighty.frameRate // 30



// Stored Property (값이 저장되어 있는 프로퍼티)


struct FixedLengthRange {

    var firstValue: Int

    let length: Int

}


var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)

rangeOfThreeItems.firstValue = 6


let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)

//rangeOfFourItems.firstValue = 6  // 값을 바꿀 수 없다고 에러 발생




// Computed Property (값을 저장하지 않고 매번 새로운 값을 개선하는 프로퍼티)


struct Point {

    var x = 0.0

    var y = 0.0

}


struct Size {

    var width = 0.0

    var height = 0.0

}


struct Rect {

    var origin = Point()

    var size = Size()

    var center: Point {

        get {

            let centerX = origin.x + (size.width / 2)

            let centerY = origin.y + (size.height / 2)

            return Point(x: centerX, y: centerY)

        }

        set(newCenter) {

            origin.x = newCenter.x - (size.width / 2)

            origin.y = newCenter.y - (size.height / 2)

        }

    }

}


var square = Rect(origin: Point(x: 0.0, y: 0.0), size: Size(width: 10, height: 10))

let initialCenter = square.center

square.center = Point(x: 15.0, y: 15.0)

square.origin


// Type Property ( 인스턴스에 귀속되어 있지 않고 타입 자체에 연관되어 있는 프로퍼티)











//

//  AppDelegate.swift

//  Todos

//

//  Created by stayfoolish on 08/10/2018.

//  Copyright © 2018 stayfoolish. All rights reserved.

//


import UIKit

import UserNotifications


@UIApplicationMain

class AppDelegate: UIResponder, UIApplicationDelegate {


    var window: UIWindow?



    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

        

        // User Notification Center를 통해서 노티피케이션 권한 획득

        let center: UNUserNotificationCenter = UNUserNotificationCenter.current()

        center.requestAuthorization(options: [UNAuthorizationOptions.alert, UNAuthorizationOptions.sound,

                                              UNAuthorizationOptions.badge]) { (granted, error) in

                                                print("허용여부 \(granted), 오류 : \(error?.localizedDescription ?? "없음")")

        }

        

        // 맨 처음 화면의 뷰 컨트롤러(TodosTableViewController)를 UserNotificationCenter의 delegate로 설정

        if let navigationController: UINavigationController = self.window?.rootViewController as? UINavigationController,

            let todosTableViewController: TodosTableViewController = navigationController.viewControllers.first as? TodosTableViewController {

            

            UNUserNotificationCenter.current().delegate = todosTableViewController

        }

        

        return true

    }


    func applicationWillResignActive(_ application: UIApplication) {

        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.

        // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.

    }


    func applicationDidEnterBackground(_ application: UIApplication) {

        // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.

        // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.

    }


    func applicationWillEnterForeground(_ application: UIApplication) {

        // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.

    }


    func applicationDidBecomeActive(_ application: UIApplication) {

        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.

    }


    func applicationWillTerminate(_ application: UIApplication) {

        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.

    }



}



//

//  TodosTableViewController.swift

//  Todos

//

//  Created by stayfoolish on 08/10/2018.

//  Copyright © 2018 stayfoolish. All rights reserved.

//


import UIKit

import UserNotifications


class TodosTableViewController: UITableViewController {

    

    // todo 목록

    private var todos: [Todo] = Todo.all

    

    // 셀에 표시할 날짜를 포맷하기 위한 Date Formatter

    private let dateFormatter: DateFormatter = {

        let formatter: DateFormatter = DateFormatter()

        formatter.dateStyle = DateFormatter.Style.medium

        formatter.timeStyle = DateFormatter.Style.short

        return formatter

    }()


    override func viewDidLoad() {

        super.viewDidLoad()

        

        // UIViewController에서 제공하는 기본 수정버튼

        self.navigationItem.leftBarButtonItem = self.editButtonItem

    }


    override func viewWillAppear(_ animated: Bool) {

        super.viewWillAppear(animated)

        

        // 화면이 보여질 때마다 todo 목록을 새로고침

        self.todos = Todo.all

        self.tableView.reloadSections(IndexSet(integer: 0), with: UITableViewRowAnimation.automatic)

    }

    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

        // Dispose of any resources that can be recreated.

    }


    // MARK: - Table view data source

    /// 테이블뷰의 섹션 수 (기본값 1)

    override func numberOfSections(in tableView: UITableView) -> Int {

        return 1

    }


    /// 테이블뷰의 섹션 별 로우 수

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        // #warning Incomplete implementation, return the number of rows

        return self.todos.count

    }


    /// 인덱스에 해당하는 cell 반환

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        

        // 스토리보드에 구현해 둔 셀을 재사용 큐에서 꺼내옴

        let cell = tableView.dequeueReusableCell(withIdentifier: "todoCell", for: indexPath)

        

        guard indexPath.row < self.todos.count else { return cell }


        let todo: Todo = self.todos[indexPath.row]


        // 셀에 내용 설정

        cell.textLabel?.text = todo.title

        cell.detailTextLabel?.text = self.dateFormatter.string(from: todo.due)

        

        return cell

    }

    


    /*

    // Override to support conditional editing of the table view.

    override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {

        // Return false if you do not want the specified item to be editable.

        return true

    }

    */


    /*

    // Override to support editing the table view.

    override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {

        if editingStyle == .delete {

            // Delete the row from the data source

            tableView.deleteRows(at: [indexPath], with: .fade)

        } else if editingStyle == .insert {

            // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view

        }    

    }

    */


    /*

    // Override to support rearranging the table view.

    override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {


    }

    */


    /*

    // Override to support conditional rearranging of the table view.

    override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {

        // Return false if you do not want the item to be re-orderable.

        return true

    }

    */


    

    // MARK: - Navigation


    // In a storyboard-based application, you will often want to do a little preparation before navigation

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

        guard let todoViewController: TodoViewController = segue.destination as? TodoViewController else {

            return

        }

        

        guard let cell: UITableViewCell = sender as? UITableViewCell else { return }

        guard let index: IndexPath = self.tableView.indexPath(for: cell ) else { return }

        

        guard index.row < todos.count else { return }

        let todo: Todo = todos[index.row]

        todoViewController.todo = todo

    }

}


// User Notification의 delegate 메서드 구현

extension TodosTableViewController: UNUserNotificationCenterDelegate {

    

    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {

        

        let idToshow: String = response.notification.request.identifier

        

        guard let todoToShow: Todo = self.todos.filter({ (todo: Todo) -> Bool in

            return todo.id == idToshow

        }).first else {

            return

        }

        

        guard let todoViewController: TodoViewController = self.storyboard?.instantiateViewController(withIdentifier: TodoViewController.storyboardID) as? TodoViewController else { return }

        

        todoViewController.todo = todoToShow

        

        self.navigationController?.pushViewController(todoViewController, animated: true)

        UIApplication.shared.applicationIconBadgeNumber = 0

        

        completionHandler()

    }

}




//

//  TodoViewController.swift

//  Todos

//

//  Created by stayfoolish on 08/10/2018.

//  Copyright © 2018 stayfoolish. All rights reserved.

//


import UIKit


class TodoViewController: UIViewController {

    

    

    /// 동일한 화면을 편집상태와 보기 모드로 변환

    private enum Mode {

        case edit, view

    }

    

    /// 스토리보드에 구현해 둔 인스턴스를 코드를 통해 더 생성하기 위하여 스토리보드 ID를 활용

    static let storyboardID: String = "TodoViewController"

    

    /// 화면에 보여줄 Todo 정보

    var todo: Todo?

    

    /// 현재 화면의 작업상태

    private var mode: Mode = Mode.edit{

        // mode 변경에 따라 적절한 처리

        didSet {

            self.titleField.isUserInteractionEnabled = (mode == .edit)

            self.memoTextView.isEditable = (mode == .edit)

            self.dueDatePicker.isUserInteractionEnabled = (mode == .edit)

            self.shouldNotifySwitch.isEnabled = (mode == .edit)

            

            if mode == Mode.edit {

                if todo == nil {

                    self.navigationItem.leftBarButtonItems = [self.cancelButton]

                }else {

                    self.navigationItem.rightBarButtonItems = [self.doneButton, self.cancelButton]

                }

            } else {

                self.navigationItem.rightBarButtonItems = [self.editButton]

            }

        }

    }

    

    /// 수정 - 내비게이션 바 버튼

    private var editButton: UIBarButtonItem {

        let button: UIBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.edit, target: self, action: #selector(touchUpEditButton(_:)))

        return button

    }

    

    /// 취소 - 내비게이션 바 버튼

    private var cancelButton: UIBarButtonItem {

        let button: UIBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.cancel, target: self, action: #selector(touchUpCancelButton(_:)))

        return button

    }

    

    /// 완료 - 내비게이션 바 버튼

    private var doneButton: UIBarButtonItem {

        let button: UIBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.done, target: self, action: #selector(touchUpDoneButton(_:)))

        return button

    }

    

    @IBOutlet weak var titleField: UITextField!

    @IBOutlet weak var memoTextView: UITextView!

    @IBOutlet weak var dueDatePicker: UIDatePicker!

    @IBOutlet weak var shouldNotifySwitch: UISwitch!

    

    /// 화면초기화

    private func initializeViews(){

        

        // 이전화면에서 전달받은 todo가 있다면 그에 맞게 화면 초기화

        if let todo: Todo = self.todo {

            self.navigationItem.title = todo.title

            self.titleField.text = todo.title

            self.memoTextView.text = todo.memo

            self.dueDatePicker.date = todo.due

            self.mode = Mode.view

        }

    }

    

    /// 간단한 얼럿을 보여줄 때 코드 중복을 줄이기위한 메서드

    private func showSimpleAlert(message: String,

                                 cancelTitle: String = "확인",

                                 cancelHandler: ((UIAlertAction) -> Void)? = nil) {

        let alert: UIAlertController = UIAlertController(title: "알림", message: message, preferredStyle: UIAlertControllerStyle.alert)

        

        let action: UIAlertAction = UIAlertAction(title: cancelTitle, style: UIAlertActionStyle.cancel, handler: cancelHandler)

        

        alert.addAction(action)

        self.present(alert, animated: true, completion: nil)

    }

    

    /// 수정 버튼을 눌렀을 때

    @objc private func touchUpEditButton(_ sender: UIBarButtonItem){

        self.mode = Mode.edit

    }

    

    /// 취소 버튼을 눌렀을 때

    @objc private func touchUpCancelButton(_ sender: UIBarButtonItem){

        if self.todo == nil {

            // 이전 화면에서 전달받은 todo가 없다면 새로 작성을 위한 상태이므로 모달을 내려주고

            self.navigationController?.presentingViewController?.dismiss(animated: true, completion: nil )

        } else {

            // 그렇지 않으면 다시 원래 todo 상태로 화면을 초기화 해줌

            self.initializeViews()

        }

    }

    

    /// 완료 버튼을 눌렀을 때

    @objc private func touchUpDoneButton(_ sender: UIBarButtonItem){

        

        // todo 제목은 필수사항이므로 입력했는지 확인

        guard let title: String = self.titleField.text, title.isEmpty == false else {

            self.showSimpleAlert(message: "제목은 꼭 작성해야 합니다", cancelHandler: {(action: UIAlertAction) in

                self.titleField.becomeFirstResponder()

            })

            return

        }

        

        // 새로운  todo 생성

        let todo: Todo

        todo = Todo(title: title, due: self.dueDatePicker.date, memo: self.memoTextView.text, shouldNotify: self.shouldNotifySwitch.isOn , id: self.todo?.id ?? String(Date().timeIntervalSince1970)) /// 유닉스 타임스템프를 할 일 고유 아이디로 활용

        let isSuccess: Bool

        

        if self.todo == nil {

            // 새로 작성하기 위한 상태라면 저장을 완료하고 모달을 내려줌

            isSuccess = todo.save {

                self.navigationController?.presentingViewController?.dismiss(animated: true, completion: nil)

            }

        } else {

            // 수정상태라면 저장을 완료하고 화면을 보기모드로 전환

            isSuccess = todo.save(completion: {

                self.todo = todo

                self.mode = Mode.view

            })

        }

        

        // 저장에 실패하면 알림

        if isSuccess == false {

            self.showSimpleAlert(message: "저장 실패")

        }

    }

    

    override func viewDidLoad() {

        super.viewDidLoad()

        

        // 텍스트 필드 delegate 설정

        self.titleField.delegate = self

        

        // 이전 화면에서 전달받은 todo가 없다면 새로운 작성화면 설정

        if self.todo == nil {

            self.navigationItem.leftBarButtonItem = self.cancelButton

            self.navigationItem.rightBarButtonItem = self.doneButton

        } else {

            self.navigationItem.rightBarButtonItem = self.editButton

        }

        

        // 화면 초기화

        self.initializeViews()

    }

    

    override func viewDidAppear(_ animated: Bool) {

        super.viewDidAppear(animated)

        

        // 수정 모드라면 텍스트 필드에 바로 입력할 수 있도록 키보드 보여줌

        if self.mode == Mode.edit {

            self.titleField.becomeFirstResponder()

        }

    }


    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

        // Dispose of any resources that can be recreated.

    }


}


/// 텍스트 필드 delegate 메서드 구현

extension TodoViewController: UITextFieldDelegate {

    func textFieldDidEndEditing(_ textField: UITextField) {

        self.navigationItem.title = textField.text

    }

}



//

//  Todo.swift

//  Todos

//

//  Created by stayfoolish on 12/10/2018.

//  Copyright © 2018 stayfoolish. All rights reserved.

//


import Foundation

import UserNotifications


struct Todo: Codable {

    var title: String           // 작업이름

    var due: Date               // 작업기한

    var memo: String?           // 작업메모

    var shouldNotify: Bool      // 사용자가 기한에 맞춰 알림을 받기 원하는지

    var id: String              // 작업 고유 ID

}


// Todo 목록 저장/로드

extension Todo {

    

    static var all: [Todo] = Todo.loadTodosFromJSONFile()

    

    // Todo JSON 파일 위치

    private static var todosPathURL: URL {

        return try! FileManager.default.url(for: FileManager.SearchPathDirectory.applicationSupportDirectory, in: FileManager.SearchPathDomainMask.userDomainMask, appropriateFor: nil, create: true).appendingPathComponent("todos.json")

    }

    

    // JSON 파일로부터 Todo 배열 읽어오기

    private static func loadTodosFromJSONFile() -> [Todo] {

        do {

            let jsonData: Data = try Data(contentsOf: self.todosPathURL)

            let todos: [Todo] = try JSONDecoder().decode([Todo].self, from: jsonData)

            return todos

        } catch {

            print(error.localizedDescription)

        }

        return []

    }

    

    // 현재 Todo 배열 상태를 JSON 파일로 저장

    @discardableResult private static func  saveToJSONFile() -> Bool {

        do {

            let data: Data = try JSONEncoder().encode(self.all)

            try data.write(to: self.todosPathURL, options: Data.WritingOptions.atomicWrite)

            return true

        } catch {

            print(error.localizedDescription)

        }

        return false

    }

}


// 현재 Todo 배열에 추가/삭제/수정

extension Todo {

    

    @discardableResult static func remove(id: String) -> Bool {

        

        guard let index: Int = self.all.index(where: { (todo: Todo) -> Bool in

            todo.id == id

        }) else { return false}

        self.all.remove(at: index)

        return self.saveToJSONFile()

    }

    

    @discardableResult func save(completion: () -> Void) -> Bool {

        

        if let index = Todo.index(of: self) {

            Todo.removeNotification(todo: self)

            Todo.all.replaceSubrange(index...index, with: [self])

        } else {

            Todo.all.append(self)

        }

        

        let isSuccess: Bool = Todo.saveToJSONFile()

        

        if isSuccess{

            if self.shouldNotify {

                Todo.addNotification(todo: self)

            }else {

                Todo.removeNotification(todo: self)

            }

            completion()

        }

        

        return isSuccess

    }

    

    private static func index(of target: Todo) -> Int? {

        guard let index: Int = self.all.index(where: { (todo: Todo) -> Bool in

            todo.id == target.id

        }) else { return nil }

        

        return index 

    }

}




/// Todo의 User Notification 관련 메서드

extension Todo {

    

    private static func addNotification(todo: Todo) {

        // 공용 UserNotification 객체

        let center: UNUserNotificationCenter = UNUserNotificationCenter.current()

        

        

        

        // 노티피케이션 콘텐츠 객체 생성

        let content = UNMutableNotificationContent()

        content.title = "할일 알림"

        content.body = todo.title

        content.sound = UNNotificationSound.default()

        content.badge = 1

        

        // 기한 날짜 생성

        let dateInfo = Calendar.current.dateComponents([Calendar.Component.year, Calendar.Component.day, Calendar.Component.hour, Calendar.Component.minute], from: todo.due )

        

        // 노티피케이션 트리거 생성

        let trigger = UNCalendarNotificationTrigger(dateMatching: dateInfo, repeats: false)

        

        // 노티피케이션 요청 객체 생성

        let request = UNNotificationRequest(identifier: todo.id, content: content, trigger: trigger)

        

        // 노티피케이션 스케줄 추가

        center.add(request, withCompletionHandler: { (error : Error?) in

            if let theError = error {

                print(theError.localizedDescription)

            }

        })

    }

    

    private static func removeNotification(todo: Todo) {

        let center: UNUserNotificationCenter = UNUserNotificationCenter.current()

        center.removePendingNotificationRequests(withIdentifiers: [todo.id])

    }

}




/*

 함수 : 특정 작업을 수행하는 독립적인 코드 덩어리

 

 예)

 커피 내리기

 1. 물을 끓인다.

 2. 커피 공을 간다.

 3. 필터에 넣고 물을 흘려보낸다.

 4. 커피를 따뜻하게 유지시킨다.

 ...

 

 DRY : Don't Repeat Yourself

 */


// Wall #1

let width = 25

let height = 7

let area = width * height


// Wall #2

let secondWidth = 14

let secondHeight = 3

let secondArea = secondWidth * secondHeight


func calculateArea() {

    let width = 25

    let height = 7

    let area = width * height

    print(area)

}


calculateArea() // 175


func calculateArea2(width: Int) {

    let height = 7

    let area = width * height

    print(area)

}


calculateArea2(width: 10) // 70

calculateArea2(width: 20) // 140



func calculateArea3(width: Int, height: Int ) {

    let area = width * height

    print(area)

}


// Wall #3

calculateArea3(width: 17, height: 10) // 170


// Wall #4

calculateArea3(width: 14, height: 3) // 42


func calculaterArea4(width: Int ,height: Int ) -> Int {

    let area = width * height

    return area

}


let areaOfWall1 = calculaterArea4(width: 17, height: 10)


// Hint : Double

// C = (F-32) * 5/9


func convertToCelsius (temperature: Double ) -> Double {

    let temperatureInCelsius = (temperature - 32) * 5/9

    return temperatureInCelsius

}


let C = convertToCelsius(temperature: 80)

print(C) // 26.6666666666667


func convertToCelsius2 (temperatureInFahrenheit temp: Double ) -> Double {

    let temperatureInCelsius = (temp - 32) * 5/9

    return temperatureInCelsius

}


convertToCelsius2(temperatureInFahrenheit: 80)


func sayHello(to person: String, and anotherPerson: String) -> String {

    return "Hello \(person) and \(anotherPerson)"

}


sayHello(to: "Jack", and: "Jill")


let sayHello1 = sayHello(to: "Jack", and: "Jill")

print(sayHello1) // "Hello Jack and Jill\n"


func calculateWallpaperPrice(area: Int , color: String) -> Int {

    var price: Int

    switch color {

    case "beige": price = area * 10000

    case "gray": price = area * 15000

    case "pink": price = area * 20000

    default: price = 0

    }

    return price

}


// 함수 areaOfWall1 를 호출해서 area에 넣어준다.

calculateWallpaperPrice(area: areaOfWall1, color: "beige")



func calculateWallpaperPrice2 (width: Int ,height: Int  , color: String) -> Int {

    var price: Int

    

    let area = calculaterArea4(width: width, height: height)

    

    switch color {

    case "beige": price = area * 10000

    case "gray": price = area * 15000

    case "pink": price = area * 20000

    default: price = 0

    }

    return price

}



calculateWallpaperPrice2(width: 17, height: 10, color: "beige")



func calculateWallpaperPrice3 (width: Int ,height: Int  , color: String = "beige" ) -> Int {

    var price: Int

    

    let area = calculaterArea4(width: width, height: height)

    

    switch color {

    case "beige": price = area * 10000

    case "gray": price = area * 15000

    case "pink": price = area * 20000

    default: price = 0

    }

    return price

}


// 함수의 파라미터 기본값을 설정하면 아래와 같이 2개 값만 입력 가능하게 나온다.

//calculateWallpaperPrice3(width: <#T##Int#>, height: <#T##Int#>)

//calculateWallpaperPrice3(width: <#T##Int#>, height: <#T##Int#>, color: <#T##String#>)

calculateWallpaperPrice3(width: 15, height: 10) // 1500000


func calculateWallpaperPrice4 (width: Int ,height: Int  , color: String = "beige" ) -> (Int, String) {

    var price: Int

    

    let area = calculaterArea4(width: width, height: height)

    

    switch color {

    case "beige": price = area * 10000

    case "gray": price = area * 15000

    case "pink": price = area * 20000

    default: price = 0

    }

    return (price,color)

}



let result = calculateWallpaperPrice4(width: 14, height: 13)  // (.0 1820000, .1 "beige")

print(result) // "(1820000, "beige")\n"

print(result.0) // "1820000\n"

print(result.1) // "beige\n"


func calculateWallpaperPrice5 (width: Int ,height: Int  , color: String = "beige" ) -> (price: Int, color: String) {

    var price: Int

    

    let area = calculaterArea4(width: width, height: height)

    

    switch color {

    case "beige": price = area * 10000

    case "gray": price = area * 15000

    case "pink": price = area * 20000

    default: price = 0

    }

    return (price,color)

}



let result2 = calculateWallpaperPrice5(width: 12, height: 11) // (price 1320000, color "beige")

print(result2.price) // "1320000\n"

print(result2.color) // "beige\n"



// 함수 스코프 scope

func incrementAndDecrement(a: Int) {

    var a = a

    var b = a

    

    a += 1

    b -= 1

}


let a = 20

incrementAndDecrement(a: 20)

a // 20



스위프트 고차함수 swift higher-order function



import Swift


/*

 - 고차함수

 전달인자로 함수를 전달받거나

 함수실행의 결과를 함수로 반환하는 함수

 

 map, filter, reduce

 

 */


// - map

// 컨테이너 내부의 기존 데이터를 변형(transform)하여 새로운 컨테이너 생성


let numbers: [Int] = [0,1,2,3,4]

var doubledNumbers: [Int]

var strings: [String]


// for 구문 사용

doubledNumbers = [Int]()

strings = [String]()


for number in numbers {

    doubledNumbers.append(number * 2)

    strings.append("\(number)")

}


print(doubledNumbers) // [0,2,4,6,8]

print(strings) // ["0","1","2","3","4"]


// map 메서드 사용

// numbers의 각 요소를 2배하여 새로운 배열 반환

doubledNumbers = numbers.map({ (number: Int) -> Int in

    return number * 2

})


// numbers의 각 요소를 문자열로 변환하여 새로운 배열 반환

strings = numbers.map({ ( number: Int) -> String in

    return "\(number)"

})


print(doubledNumbers)

print(strings)



// 매개변수, 반환 타입, 반환 키워드(return) 생략, 후행 클로저

doubledNumbers = numbers.map { $0 * 2 }


print(doubledNumbers)


// - filter

// 컨테이너 내부의 값을 걸러서 새로운 컨테이너로 추출


// for 구문 사용

// 변수 사용에 주목하세요

var filtered: [Int] = [Int]()


for number in numbers {

    if number % 2 == 0 {

        filtered.append(number)

    }

}


print(filtered)


// filter 메서드 사용

// numbers의 요소 중 짝수를 걸러내어 새로운 배열로 반환

let evenNumbers: [Int] = numbers.filter {

    (number: Int) -> Bool in

    return number % 2 == 0

}


print(evenNumbers) // [0, 2, 4]


// 매개변수, 반환 타입, 반환 키워드(return) 생략, 후행 클로저

let oddNumbers: [Int] = numbers.filter{

    $0 % 2 != 0

}


print(oddNumbers) // [1, 3]


//MARK: - reduce

// 컨테이너 내부의 콘텐츠를 하나로 통합


let someNumbers: [Int] = [2, 8, 15]


//MARK: for 구문 사용

//변수 사용에 주목하세요

var result: Int = 0


// someNumbers의 모든 요소를 더합니다.

for number in someNumbers {

    result += number

}


print(result) // 25


//MARK: reduce 메서드 사용

// 초기값이 0 이고 someNumbers 내부의 모든 값을 더합니다.

let sum: Int = someNumbers.reduce(0, {

    (first: Int, second: Int) -> Int in

    return first + second

})


print(sum) // 25


// 초기값이 0이고 someNumbers 내부의 모든 값을 뺍니다.

var subtract: Int = someNumbers.reduce(0, {

    (first: Int , second: Int ) -> Int in

    return first - second

})


print(subtract) // -25


// 초기값이 3이고 someNumbers 내부의 모든 값을 더합니다.

let sumFromThree = someNumbers.reduce(3) { $0 + $1}


print(sumFromThree) // 28


// 모든 값을 곱하고 싶을 때

print(someNumbers.reduce(1) { $0 * $1}) // 240



스위프트 오류처리 swift Error




import Swift


/*

 Error 프로토콜과 (주로) 열거형을 통해서 오류를 표현합니다.

 


 enum 오류종류이름: Error {

    case 종류1

    case 종류2

    case 종류3

 }

 

 */



// 자판기 동작 오류의 종류를 표현한 VendingmachinError 열거형


enum VendingMachineError: Error {

    case invalidInput

    case insufficientFunds(moneyNeeded: Int)

    case outOfStock

}


/*

 자판기 동작 도중 발생한 오류 던지기

 오류 발생의 여지가 있는 메서드는 throws를 사용하여

 오류를 내포하는 함수임을 표시합니다.

 */


class VendingMachine {

    let itemPrice: Int = 100

    var itemCount: Int = 5

    var deposited: Int = 0

    

    // 돈 받기 메서드

    func receiveMoney(_ money: Int ) throws {

        

        // 입력한 돈이 0 이하면 오류를 던집니다.

        guard money > 0  else {

            throw VendingMachineError.invalidInput

        }

        

        // 오류가 없으면 정상처리를 합니다.

        self.deposited += money

        print("\(money)원 받음")

    } // 300원 받음

    

    // 물건 팔기 메서드

    func vend(numberOfItems numberOfItemsToVend: Int) throws -> String {

        

        // 원하는 아이템의 수량이 잘못 입력되었으면 오류를 던집니다.

        guard numberOfItemsToVend > 0 else {

            throw VendingMachineError.invalidInput

        }

        

        // 구매하려는 수량보다 미리 넣어둔 돈이 적으면 오류를 던집니다.

        guard numberOfItemsToVend * itemPrice <= deposited else {

            let moneyNeeded: Int

            moneyNeeded = numberOfItemsToVend * itemPrice - deposited

            

            throw VendingMachineError.insufficientFunds(moneyNeeded: moneyNeeded)

        }

        

        // 구매하려는 수량보다 요구하는 수량이 많으면 오류를 던집니다.

        guard itemCount >= numberOfItemsToVend else {

            throw VendingMachineError.outOfStock

        }

        

        // 오류가 없으면 정상처리를 합니다.

        let totalPrice = numberOfItemsToVend * itemPrice

        

        self.deposited -= totalPrice

        self.itemCount -= numberOfItemsToVend

        

        return "\(numberOfItemsToVend)개 제공함"

    }

}


// 자판기 인스턴스

let machine: VendingMachine = VendingMachine()


// 판매 결과를 전달받은 변수

var result: String?


/*

 - 오류처리

오류발생의 여지가 있는 throws 함수(메서드)는

try를 사용하여 호출해야 합니다.

try, try?, try!

 

 - do-catch

 오류발생의 여지가 있는 throws 함수(메서드)는

 do-catch 구문을 활용하여 오류발생에 대비합니다.

 

 */


do {

    try machine.receiveMoney(0)

}catch VendingMachineError.invalidInput {

    print("입력이 잘못되었습니다.")

}catch VendingMachineError.insufficientFunds(let moneyNeeded){

    print("\(moneyNeeded)원이 부족합니다")

}catch VendingMachineError.outOfStock {

    print("수량이 부족합니다")

} // 입력이 잘못되었습니다.


do {

    try machine.receiveMoney(300)

}catch /*(let error)*/ {

    switch error {

    case VendingMachineError.invalidInput:

        print("입력이 잘못되었습니다.")

    case VendingMachineError.insufficientFunds(let moneyNeeded):

        print("\(moneyNeeded)원이 부족합니다")

    case VendingMachineError.outOfStock:

        print("수량이 부족합니다")

    default:

        print("알수없는 오류 \(error)")

        

    }

} // 300원 받음


do {

    result = try machine.vend(numberOfItems: 4)

}catch {

    print(error)

} // insufficientFunds(100)


do {

    result = try machine.vend(numberOfItems: 4)

}



/*

 try?

 별도의 오류 처리 결과를 통보받지 않고

 오류가 발생했으면 결과값을 nil로 돌려받을 수 있습니다.

 정상동작 후에는 옵셔널 타입으로 정상 반환값을 돌려 받습니다.

 */


result = try? machine.vend(numberOfItems: 2)

result // Optional("2개 제공함")


result = try? machine.vend(numberOfItems: 2)

result // nil



/*

 try!

 오류가 발생하지 않을 것이라는 강력한 확신을 가질 때

 try!를 사용하면 정상동작 후에 바로 결과값을 돌려받습니다.

 오류가 발생하면 런타입 오류가 발생하여

 애플리케이션 동작이 중지됩니다.

 

 */


result = try! machine.vend(numberOfItems: 1)

result // 1개 제공함


//result = try! machine.vend(numberOfItems: 1)


+ Recent posts