Things take time

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

iOS (교육)

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

겸손할 겸 2017. 4. 1. 17:56

[SWIFT]


1. 현재 시간과 선택 시간(데이트 피커 뷰)이 일치하면 Alert, 경고 창을 띄우며 알겠다는 문구를 입력하면 1분동안 나타나지 않도록 하는 앱 만들기

    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)")
        
        if(alamTime == currentTime){
            if !isCheck{
                let alert = UIAlertController(title:"알림", message: "설정된 시간입니다", preferredStyle: .alert)
                alert.addAction(UIAlertAction(title: "네, 알겠습니다", style: .cancel, handler: nil))
                self.present(alert, animated: true, completion: nil)
                isCheck = true
            } else {
                isCheck = false
            }
        }
    }

- 기존 datePicker 앱에서 updateLabel()만 수정

- Alert는 기본적으로 Modal 뷰라고 하여 띄워진 뷰 뒤의 백그라운드가 눌려지지 않는 뷰를 사용한다.

- present를 통해 띄워진 뷰는 dismiss라는 것을 이용하여 내릴 수 있음



2. 웹뷰


import UIKit class ViewController: UIViewController, UIWebViewDelegate { @IBOutlet var urlTextField: UITextField! @IBOutlet var webView: UIWebView! @IBOutlet var indicatorView: UIActivityIndicatorView! override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. webView.delegate = self } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } @IBAction func goClick(_ sender: UIButton) { let urlString = urlTextField.text webView.loadRequest(URLRequest(url: URL(string: urlString!)!)) } @IBAction func GoNaver(_ sender: UIButton) { webView.loadRequest(URLRequest(url: URL(string: "http://m.naver.com")!)) } @IBAction func GoDaum(_ sender: Any) { webView.loadRequest(URLRequest(url: URL(string: "http://m.daum.net")!)) } @IBAction func loadHTML(_ sender: UIButton) { let htmlString: String = 주석참고 webView.loadHTMLString(htmlString, baseURL: nil) } @IBAction func loadFile(_ sender: UIButton) { if let path = Bundle.main.path(forResource: "file", ofType: "html") { webView.loadRequest(URLRequest(url: URL(string: path)!)) } } // 페이지를 열지 말지를 결정하는, 직전에 호출되는 함수 @available(iOS 2.0, *) public func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool{ print(request.url!) // NSString타입이 String타입보다 내장 메소드를 더 갖고 있기 때문에 형변환 한 것 if let urlString = request.url?.absoluteString as NSString?{ if urlString.contains("daum.net") { return false } } return true } @available(iOS 2.0, *) public func webViewDidStartLoad(_ webView: UIWebView){ indicatorView.startAnimating() } @available(iOS 2.0, *) public func webViewDidFinishLoad(_ webView: UIWebView){ indicatorView.stopAnimating() } @available(iOS 2.0, *) public func webView(_ webView: UIWebView, didFailLoadWithError error: Error){ indicatorView.stopAnimating() // 에러 출력 시 print(error.localizedDescription) } /* 인디케이터 제어 방법 : 코드로 제어 @IBAction func stopWebView(_ sender: UIBarButtonItem) { webView.stopLoading() } @IBAction func refreshWebView(_ sender: UIBarButtonItem) { webView.reload() } @IBAction func priorWebView(_ sender: UIBarButtonItem) { webView.goBack() } @IBAction func forwardWebView(_ sender: UIBarButtonItem) { webView.goForward() } 방법 2 : 스토리보드 상에서 해당 네 개의 버튼을 컨트롤 클릭 후 웹뷰 안으로 드래그 하면 됨 */ }

- App Transport Security has blocked ... 에러 발생 시 => plist 파일에서 +버튼 눌러 App Transport Security Setting 생성, 왼쪽의 화살표 버튼을 아래로 향하게 클릭 후 -> Allow Arbitrary Loads 를 YES로 수정 : Http로 접속시 발생하는 에러로 이 설정을 해줘야 함

- HTMLString = "<h1> HTML String </h1> <p>String 변수를 이용한 페이지</p> <p><a href=\"http://www.google.com\">Google 홈페이지</a> 이동 </p>"

- file.html을 empty file로 만들어서 직접 작성한 후, 부르는 방법 참고 => Bundle.main XXX, 그리고 해당 파일의 여부를 조사하는 if문을 넣어주는 것이 안정성면에서 좋음



3. 맵뷰, Segmented Control

- 들어가는 개수에 따라 증가되는 Segmented Control에서 각 글자수에 맞춰 너비를 다르게 하고 싶다면 위의 Proportinal to Content로 설정함

- 지도와 같은 기능 사용시엔 사용 설명 없이 사용할 수 없도록 되어있음 -> plist에 포함해야 함(NSLocationWhenInUseUsageDescription)

import UIKit
import MapKit

class ViewController: UIViewController, CLLocationManagerDelegate {

    // strong : heap 저장
    // weak : 클래스 해제 시 자동 삭제
    @IBOutlet weak var mapView: MKMapView!
    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var subtitleLabel: UILabel!
    
    let locationManager = CLLocationManager()
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        
        // 딜리게이트 설정(프로토콜 상속 선행)
        locationManager.delegate = self
        // 정확도 설정
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        // 권한 요청
        locationManager.requestWhenInUseAuthorization()
        // 지도 업데이트
        locationManager.startUpdatingLocation()
        
        
        // 디바이스를 갖고있는 유저의 위치를 표시할 것인지에 대한 여부
        mapView.showsUserLocation = true
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    
    // 맵뷰에 위치 찍기
    func showLocation(latitude lat:CLLocationDegrees, longitude lot:CLLocationDegrees, delta span:Double){
        
        // 위도 경도를 갖고 location값을 만들어, setRegion에 대입
        let location = CLLocationCoordinate2DMake(lat, lot)
        let mkSpan = MKCoordinateSpanMake(span, span)
        
        mapView.setRegion(MKCoordinateRegionMake(location, mkSpan), animated: true)
    }
    
    // 마커 찍기
    func showAnnotation(latitude lat:CLLocationDegrees, longitude lot:CLLocationDegrees, delta span:Double, pinTitle title: String, pinSubTitle subTitle: String){
        let annotation = MKPointAnnotation()
        annotation.coordinate = CLLocationCoordinate2DMake(lat, lot)
        
        annotation.title = title
        annotation.subtitle = subTitle
        
        
        mapView.addAnnotation(annotation)
        
        titleLabel.text = title
        subtitleLabel.text = subTitle
    }
    
   
    @available(iOS 6.0, *)
    public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]){
        
        /*
        // guard : 옵셔널 데이터를 검사, 벗겨 낼수 없다면 else로 가기때문에 if let보다 depth가 1이 덜 깊어짐, 만약 unwrapping을 성공했다면 그 밑에 줄을 실행하게 됨
        guard let location = locations.last else {
            return
        }
        // code
        */
        
        // last : 배열 마지막 값 리턴
        guard let location = locations.last else {
            return
        }
        
        // CLGeocoder : 주소와 위도 경도를 변환해주는 클래스
        CLGeocoder().reverseGeocodeLocation(location, completionHandler: {
                (placemarks, error) -> Void in
            
            if let err = error {
                print(err.localizedDescription)
                locationManager.stopUpdatingLocation()
                return
            }
            
            
            if let unwrappedPlacemarks = placemarks, let placemark = unwrappedPlacemarks.first{
                print(placemark)
                // ?? "" : 해당 옵셔널 값이 없으면 ""로 초기화하라
                let address = String(format: "%@ %@ %@ %@ %@", placemark.country ?? "", placemark.administrativeArea ?? "", placemark.locality ?? "", placemark.thoroughfare ?? "", placemark.subThoroughfare ?? "")
                
                
                self.showLocation(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude, delta: 0.01)
                self.showAnnotation(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude, delta: 0.01, pinTitle:"현재위치", pinSubTitle: address)
            }
        })
        
        locationManager.stopUpdatingLocation()
    }
    
    
    @IBAction func changeSeg(_ sender: UISegmentedControl) {
        print(sender.selectedSegmentIndex)
        if sender.selectedSegmentIndex == 0{ // 현재 위치
            locationManager.startUpdatingLocation()
        } else if sender.selectedSegmentIndex == 1{ // 강남역
            showLocation(latitude: 37.498248, longitude: 127.027621, delta: 0.01)
            showAnnotation(latitude: 37.498248, longitude: 127.027621, delta: 0.01, pinTitle: "강남역", pinSubTitle: "이다")
        } else if sender.selectedSegmentIndex == 2{ // 집
            showLocation(latitude: 37.458329, longitude: 127.160148, delta: 0.01)
            showAnnotation(latitude: 37.458329, longitude: 127.160148, delta: 0.01, pinTitle: "집", pinSubTitle: "이다")
        }
    }

}

- 중요 부분 : 마커 찍기, 현재 위치 로딩, guard let, 핸들러 부분



4. 페이지 컨트롤 : 페이지 컨트롤 사용할 때, 화면의 좌우를 꽉차게 해야 옆 부분 클릭해도 잘 넘어감, 근데 스왑(터치)으로 넘어가는 방식을 더 사용하기에 잘 사용하지 않는 방식

import UIKit

class ViewController: UIViewController {
    var imageArray = Array()
    
    @IBOutlet weak var imageView: UIImageView!
    @IBOutlet weak var pageControl: UIPageControl!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        for i in 1...6{
            imageArray.append(String(format: "%02d.png",i))
        }
        
        imageView.image = UIImage(named: imageArray[0])
        pageControl.numberOfPages = imageArray.count
        pageControl.currentPage = 0
        pageControl.pageIndicatorTintColor = UIColor.red
        pageControl.currentPageIndicatorTintColor = UIColor.yellow
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    @IBAction func changepage(_ sender: UIPageControl) {
        imageView.image = UIImage(named: imageArray[sender.currentPage])
    }

}



5. 페이지 컨트롤2 : 좌우로 스왑하면서 아래 페이지 컨트롤이 자동적으로 변할 수 있도록

import UIKit

class ViewController: UIViewController, UIScrollViewDelegate {
    @IBOutlet weak var scrollView: UIScrollView!
    var pageControl = UIPageControl()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        scrollView.delegate = self
        scrollView.contentSize = CGSize(width: view.frame.width * 6, height: scrollView.frame.height)
        
        
        // 가로로 긴 스크롤 뷰를 만들 것
        for i in 0...5{
            // x, y는 생성할 위치의 시작 저좌표 값
            let imageView = UIImageView(frame: CGRect(x: CGFloat(i) * view.frame.width , y: 0, width: view.frame.width, height: view.frame.height))
            imageView.image = UIImage(named: String(format: "%02d.png", i+1))
            scrollView.addSubview(imageView)
        }
        
        
        // 페이지 컨트롤 동적 할당하기
        pageControl = UIPageControl(frame: CGRect(x: 0, y: scrollView.frame.height - 37, width: view.frame.width, height: 37))
        pageControl.currentPage = 0
        pageControl.numberOfPages = 6
        view.addSubview(pageControl)
        
        // 버튼 동적 할당하기
        let button = UIButton(frame: CGRect(x: 100, y: 100, width: 200, height: 150))
        button.backgroundColor = UIColor.red
        button.addTarget(self, action: #selector(self.buttonClicked), for: .touchUpInside)
        view.addSubview(button)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    func buttonClicked(sender: UIButton){
        NSLog("file info: %@", #file)
        NSLog("function info: %@", #function)
        NSLog("line info: %d", #line)
        
    }

    
    @available(iOS 2.0, *)
    public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView){
        pageControl.currentPage = (Int)(scrollView.contentOffset.x / scrollView.frame.width)
    }

}