Things take time

[SWIFT] 2일차 : 스위프트 기본 앱 본문

iOS (교육)

[SWIFT] 2일차 : 스위프트 기본 앱

겸손할 겸 2017. 3. 25. 13:21

[SWIFT]


- 교재 : 스위프트로 아이폰 앱 만들기 입문


1. ID/PW 입력 및 경고창 출력

import UIKit

class ViewController: UIViewController {
    @IBOutlet var txtID: UITextField!
    @IBOutlet var txtPW: 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) {
        let alert = UIAlertController(title: "Do it Swift", message: txtID.text! + " " + txtPW.text!, preferredStyle: .alert)
        
        
        // 클로저 함수 & Function (파라미터) -> 리턴형 in ...
        alert.addAction(UIAlertAction(title: "OK", style: .default, handler: {(action: UIAlertAction) -> Void in
            print("OK 버튼 클릭")
            }))
        
        alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: {(action: UIAlertAction) -> Void in
            print("Cancel 버튼 클릭")
        }))
        
        alert.addAction(UIAlertAction(title: "Another", style: .default, handler: {(action: UIAlertAction) -> Void in
            print("Another")
        }))
        
        self.present(alert, animated: true) {
            () -> Void in
            print("메시지 박스 출력")
        }
    }
}


- 각 컴포넌트의 Attributes inspector는 설정을 정의할 수 있음

- 비밀번호 텍스트 필드의 경우 하단의 Secure Text Entry를 설정하고, 자동 완성과 같은 기능을 제거하려면 Correction을 No로 설정

- UIAlertView 클래스를 사용하여 경고 창을 만들 수 있으나, deprecated 함수이므로 UIAlertController를 사용하길 권장

- 경고창 버튼 2개 생성 시 좌, 우로 배치되나 그 초과된 개수에 대해서는 세로로 배치 됨



2. 이미지 뷰, 축소 확대 버튼, 스위치

import UIKit class ViewController: UIViewController { var isZoom: Bool = false var imgOn: UIImage? var imgOff: UIImage? let zoomScale: CGFloat = 2.0 @IBOutlet var imgView: UIImageView! override func viewDidLoad() { super.viewDidLoad() // named 뒤에 주소를 상대적으로 받는 이유는 이미지 추가 시, 이미지가 소스폴더와 같은 위치에 있을s 경우 imgOn = UIImage(named: "lamp_on.png") imgOff = UIImage(named: "lamp_off.png") // imgView는 UIImageView 클래스 변수이며, UIImageView 클래스내에 image라는 변수가 이미지를 가리키는 객체임 imgView.image = imgOff } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } @IBAction func btnResizeImage(_ sender: UIButton) { if sender.currentTitle == "확대"{ // imgView.frame.size = CGSize(width: zoomScale, height: zoomScale) imgView.frame.size.width *= zoomScale imgView.frame.size.height *= zoomScale // for : state sender.setTitle("축소", for: .normal) }else if sender.currentTitle == "축소"{ imgView.frame.size.width /= zoomScale imgView.frame.size.height /= zoomScale sender.setTitle("확대", for: .normal) } } // 액션 연결시, UISwitch로 연결했기 때문에 isOn, isOff를 사용할 수 있음, Any선택시 어떤 클래스 객체인지 알 수 없어 사용 불가 @IBAction func switchImageOnOff(_ sender: UISwitch) { // isOn, isOff : 스위치 객체에서 내된 메소드 /* if sender.isOn{ imgView.image = imgOn } else { imgView.image = imgOff } */ // imgView.image = sender.isOn ? imgOn : imgOff /* 캐시 메모리 사용하지 않고 이미지 불러오기 let lampOnPath = Bundle.main.path(forResource: "lamp_on", ofType: "png") let lampOffPath = Bundle.main.path(forResource: "lamp_off", ofType: "png") let imgOnImage = UIImage(contentsOfFile: lampOnPath!) let imgOffImage = UIImage(contentsOfFile: lampOffPath!) imgView.image = sender.isOn ? imgOnImage : imgOffImage */ // 함수화 imgView.image = sender.isOn ? LoadImage(fileName: "lamp_on", fileType: "png") : LoadImage(fileName: "lamp_off", fileType: "png") } func LoadImage(fileName: String, fileType: String) -> UIImage?{ if let path = Bundle.main.path(forResource: fileName, ofType: fileType){ return UIImage(contentsOfFile: path) } return nil } }



- 이미지 뷰 Aspect Fit : 설정된 이미지 뷰의 크기안에 이미지를 넣을 때 이미지가 갖고 있는 비율에 맞춰 넣어줌, Aspect Fil 설정된 이미지 뷰의 크기 안에 이미지를 꽉 맞게 채우는데, 이미지를 채울 때 긴쪽이 초과 될 수 있음 (가로가 짧고 세로가 길다면, 가로에 맞춰서 이미지 뷰에 꽉 채우는데 세로가 남으므로 세로가 길게 빠져나옴)

- imgOn, imgOff란 변수를 선언해서 미리 이미지를 로드해놓는 것은 캐시로 넣어놔서 속도가 빠를 수 있으나.. 이미지 크기가 커지면 그만큼 메모리를 사용하게 되므로 그때마다 동적 할당 하는 방식이 효율적임 => keyword : contentsOfFile



3. 6개의 이미지 뷰어 만들기

import UIKit

class ViewController: UIViewController {
    @IBOutlet var imgViewer: UIImageView!
    
    var index = 0
    var imageName:Array = ["01.png", "02.png", "03.png", "04.png", "05.png", "06.png"]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        imgViewer.image = UIImage(named:imageName[index])
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    
    @IBAction func btnClickPrior(_ sender: UIButton) {
        print("\(index) / \(imageName.count)")
        if index > 0 {
            index -= 1
        } else {
            index = imageName.count-1
        }
        imgViewer.image = UIImage(named:imageName[index])
    }

    @IBAction func btnClickNext(_ sender: UIButton) {
        print("\(index) / \(imageName.count)")
        if index < imageName.count-1 {
            index += 1
        } else {
            index = 0
        }
        imgViewer.image = UIImage(named:imageName[index])
    }
}

- 결과 화면


3. 데이트 피커

import UIKit

class OutClass{
    
    @objc func outFunc(timer: Timer) -> Void{
        // userInfo로 self가 넘어왔고, self가 갖고 있는 userInfo값을 바탕으로 ViewController클래스 내부의 변수 lblCurrentTime에 접근할 수 있음
        NSLog("%@", #function)
        let vc = timer.userInfo as! ViewController
        print(vc.lblCurrentTime.text!)
    }
}

class ViewController: UIViewController {
    let dateFormat = DateFormatter()
    
    @IBOutlet var lblCurrentTime: UILabel!
    @IBOutlet var lblPickerTime: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let out = OutClass();
        
        // block : 클로져와 같음
        Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: {(
            timer: Timer) -> Void in
            
            // #function : 현재 실행 중인 함수 명
            NSLog("%@", #function)
            
            // NSDate(), Date()
            let date = Date()
            self.dateFormat.dateFormat = "yyyy-MM-dd HH:mm:ss EEE"
            
            // 클로져에서는 self를 써서 해당 객체에 접근해야 함
            self.lblCurrentTime.text = "현재 시간 : " + self.dateFormat.string(from: date)
        })
        
        // scheduledTemier의 다른 사용 방법 (객체를 넘겨 받을 때, 책과 같음)
        Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.updateLabel), userInfo: nil, repeats: true)
        
        // 다른 클래스에 있는 함수를 호출해야 할 때, target과 selector 함수는 @objc를 붙여야하며, userInfo에 self를 넘겼다는 것을 볼 것
        Timer.scheduledTimer(timeInterval: 1, target: out, selector: #selector(out.outFunc), userInfo: self, repeats: true)

    }


    @IBAction func changeDatePicker(_ sender: UIDatePicker) {
        lblPickerTime.text = "선택 시간 : " + dateFormat.string(from: sender.date)
        
    }
    
    func updateLabel() -> Void{
        NSLog("%@", #function)
    }

}

- 실행 결과


- Time.scheduledTimer라는 메소드의 세 가지 방법 : 다른 객체 전달 없을 때, 객체 전달 받을 때, 객체가 외부 클래스일 때

- Target : 클래스 객체, #selector : 클래스 객체의 Function

- userInfo에 self를 넘겼기에 ViewController로 형변환 가능함, 만약 넘기는게 label이라면 as! UILabel이 될 것



4. 선택 시간과 현재 시간 (분 까지만) 일치 시 배경 화면 바꾸기

import UIKit

class ViewController: UIViewController {
    let dateFormat = DateFormatter()
    
    var alamTime = ""
    var currentTime = ""
    var count = 0
    
    @IBOutlet var lblCurrentTime: UILabel!
    @IBOutlet var lblPickerTime: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.updateLabel), userInfo: nil, repeats: true)
    }


    @IBAction func changeDatePicker(_ sender: UIDatePicker) {
        dateFormat.dateFormat = "yyyy-MM-dd HH:mm:ss EEE"
        lblPickerTime.text = "선택 시간 : " + dateFormat.string(from: sender.date)
        
        dateFormat.dateFormat = "hh:mm aaa"
        alamTime = dateFormat.string(from: sender.date)
    }
    
    func updateLabel() -> Void{
        let date = Date()
        dateFormat.dateFormat = "yyyy-MM-dd HH:mm:ss EEE"
        lblCurrentTime.text = "현재 시간 : " + dateFormat.string(from: date)
        
        dateFormat.dateFormat = "hh:mm aaa"
        currentTime = dateFormat.string(from: date)
        
        print("\(alamTime) / \(currentTime)")
        
        self.view.backgroundColor = alamTime == currentTime ? UIColor.red : UIColor.white
    }
}

- 실행 결과


5. 피커뷰

import UIKit class ViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate { var fileNameArray: Array = Array

() @IBOutlet var imagePicker: UIPickerView! @IBOutlet var lblFileName: UILabel! @IBOutlet var imageView: UIImageView! override func viewDidLoad() { super.viewDidLoad() // self는 ViewController, dataSource는 UIPickerViewDataSource 프로토콜의 변수 // 그러므로 ViewController는 프로토콜을 상속받음 => 상속 시, 구현해야 할 함수 있는지 들어가 확인 // dataSource = 해당 컴포넌트의 정보를 설정함 (행의 개수 등), 보통 데이터소스와 델리게이트를 같이 씀 imagePicker.dataSource = self // 프로토콜을 상속받을 때 그 안에 명세된 함수를 가져다 쓰기위한 델리게이트 // delegate = 해당 컴포넌트를 갖고 할 수 있는 메소드들가져다 쓰기 위함 imagePicker.delegate = self // 배열에 이미지 넣기 for i in 1...10 { fileNameArray.append(String(format: "%d.jpg", i)) } // 초기 이미지, 라벨 설정 lblFileName.text = fileNameArray[0] imageView.image = UIImage(named: fileNameArray[0]) } // UIPickerViewDataSource 프로토콜에 명세된 함수들 => 옵셔널로 선언되지 않았다면 구현해야 함 // 피커뷰에 보여줄 컬럼의 개수 public func numberOfComponents(in pickerView: UIPickerView) -> Int { return 1 } // 피커뷰 컴포넌트 안에 갖고있는 각 데이터들의 row number public func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { return fileNameArray.count } public func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat { return 100 } /* // UIPickerViewDelegate 선택 시 타이틀 출력 public func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { return fileNameArray[row] } */ // UIPickerViewDelegate 선택 시 보여줄 뷰를 설정, 타이틀 출력과 동시 사용 안 됨 public func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView { let imageView = UIImageView() imageView.frame = CGRect(x: 0, y: 0, width: 100, height: 100) imageView.image = UIImage(named: fileNameArray[row]) return imageView } // UIPickVierDelegate : 항목 선택 시 호출 public func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { lblFileName.text = fileNameArray[row] imageView.image = UIImage(named: fileNameArray[row]) } }

- 실행 결과


6. 피커뷰 컴포넌트 2개

- 위의 소스 중, 컬럼 개수 지정함수와 클릭 후 호출할 함수만 변경

    // UIPickerViewDataSource 프로토콜에 명세된 함수들 => 옵셔널로 선언되지 않았다면 구현해야 함
    // 피커뷰에 보여줄 컬럼의 개수
    public func numberOfComponents(in pickerView: UIPickerView) -> Int
    {
        return 2
    }
    // UIPickVierDelegate : 항목 선택 시 호출
    public func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int)
    {
        if component == 0{
            lblFileName.text = fileNameArray[row]
        } else {
            imageView.image = UIImage(named: fileNameArray[row])
        }
    }


7. 경고 창

import UIKit

class ViewController: UIViewController {
    @IBOutlet var imageLamp: UIImageView!
    var lampOn:Bool = false

    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 lampOnClicked(_ sender: UIButton) {
        if(lampOn){
            let alert = UIAlertController(title: "알림", message: "램프가 이미 켜져있습니다", preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "확인", style: .default, handler: nil))
            self.present(alert, animated: true, completion: nil)
        } else {
            lampOn = true
            imageLamp.image = UIImage(named: "lamp-on.png")
        }
    }
    @IBAction func lampOffClicked(_ sender: UIButton) {
        if(lampOn){
            let alert = UIAlertController(title: "알림", message: "램프를 끄시겠습니까?", preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "네", style: .default,
                                          handler: {(action) in
                                            self.lampOn = false
                                            self.imageLamp.image = UIImage(named: "lamp-off.png")
                                                    }))
            alert.addAction(UIAlertAction(title: "아니요", style: .cancel, handler: nil))
            self.present(alert, animated: true, completion: nil)
        } else {
            imageLamp.image = UIImage(named: "lamp-off.png")
        }
    }
    @IBAction func lampRemoveClicked(_ sender: UIButton) {
        
        let alert = UIAlertController(title: "알림", message: "램프를 제거하시겠습니까?", preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "아니요, 램프를 키겠습니다", style: .default, handler: {(action) in
            self.lampOn = true
            self.imageLamp.image = UIImage(named: "lamp-on.png")
        }))
        alert.addAction(UIAlertAction(title: "아니요, 램프를 끄겠습니다", style: .cancel, handler: {(action) in
            self.lampOn = false
            self.imageLamp.image = UIImage(named: "lamp-off.png")
        }))
        alert.addAction(UIAlertAction(title: "네 램프를 제거합니다", style: .destructive, handler: {(action) in
            self.lampOn = false
            self.imageLamp.image = UIImage(named: "lamp-remove.png")
        }))
        self.present(alert, animated: true, completion: nil)
    }

}

- 실행 결과