NSNotification 예제

최상위뷰가 있다.

최상위 뷰에서 부모뷰를 띄웠다. 

부모뷰에서 자식 뷰를 띄웠다.

 

이때, 자식뷰를 닫을때 부모뷰까지 같이 닫아 줘야하는 요구사항이 있다. 이럴때는 NSNotification 을 사용한다.

 

먼저 부모뷰에 NSNotification 설정과 콜백메소드(편의상 이렇게 부르겠다)를 작성해준다.

viewdidload{

    //노티피케이션 등록 - 자식창을 닫을때 이곳(부모)도 닫히게 노티 등록

    [[NSNotificationCenter defaultCenter] addObserver:self

    selector:@selector(closeViewControllerBySelf:)

        name:@"close"

      object:nil];

}

이렇게 셋팅을 해주고 호출될 메소드를 작성해준다.

//자식창 닫힐때 부모창도 같이 닫히게

-(void) closeViewControllerBySelf:(NSNotification *) notification{

    NSLog(@"부모창이 콜되었습니다.");

    NSString *message = [notification.userInfo objectForKey:@"message"];

    NSLog(@"자식창에서 던져준 메시지 :  %@", message); //취소팝업 뷰 컨트롤러에서 던진 확인용 메시지입니다

    //화면닫기

    [self dismissViewControllerAnimated:YES completion:^{

        //부모창 닫기 성공

    }];

}

이제 자식 뷰 로직을 보자.

//닫기 버튼 눌렀을때!

- (IBAction)cancelAction:(id)sender {    

    //창 닫기

    [self dismissViewControllerAnimated:YES completion:^{

        //창이 닫힐때 작성을 해주어야 한다. 위에 작성하면 이 창을 호출한 부모창은 닫히지 않는다.

        NSDictionary *userInfo = @{ @"message": @"취소팝업 뷰 컨트롤러에서 던진 확인용 메시지입니다." };

        [[NSNotificationCenter defaultCenter] postNotificationName:@"close"

                                        object:nil

                                        userInfo:userInfo];

        // [[self view] removeFromSuperview];

    }];

}

 

자식뷰에서 창이 닫힐때 NSNotificationCenter를 이용해서 postNotificationName에해당 이름을넣고 창을 닫아주면,부모창의 NSNotificationCenter메소드가 호출된다

참고:http://seorenn.blogspot.com/2014/05/cocoa-nsnotification.html

.h

//인디케이터 객체 선언

@property (nonatomic, retain) UIActivityIndicatorView *activityIndicator;

 

.m

//인디케이터 시작

-(void)startIndicater{

    NSLog(@"인디케이터 시작");

    // ProgressBar Setting

    _activityIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 32, 32)];

    [_activityIndicator setCenter:self.view.center];

    [_activityIndicator setColor:UIColor.whiteColor];

    if (@available(iOS 13.0, *)) {

        [_activityIndicator setActivityIndicatorViewStyle:UIActivityIndicatorViewStyleLarge];

    } else {

        // Fallback on earlier versions

        [_activityIndicator setActivityIndicatorViewStyle:UIActivityIndicatorViewStyleWhiteLarge];

    }

    [self.view addSubview : _activityIndicator];

    

    // ProgressBar Start

    _activityIndicator.hidden= FALSE;

    [_activityIndicator startAnimating];

}

 

//인디케이터 종료

-(void)endIndicatter{

    NSLog(@"인디케이터 끝");

    [_activityIndicator stopAnimating];

    _activityIndicator.hidden= TRUE;   

}

.h 파일

#import <AVFoundation/AVFoundation.h>

//사운드를 재생할 오디오 플레이어객체 , 

@property (nonatomic,strong)AVAudioPlayer *player;

 

.m 파일

    /*

     로컬에 sound 파일 재생하기 위한 경로를 가져온다.

     경로를 url 타입으로 변환 - > avaudioplayer로 재생.

     */

    NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:@"sos" ofType:@"wav"];

    NSURL *soundFileURL = [NSURL fileURLWithPath:soundFilePath];

    NSLog(@"soundFileURL : %@" , soundFileURL);

    NSError *error;

    self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:soundFileURL  error:&error];

    self.player.numberOfLoops = -1; //반복

    [self.player play];

Ios autoLayout 기초

장치가 세로일때 뷰 모양
장치가 가로일때 뷰모양

예제소스


#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
  
    
    //크기 고정 뷰 - 화면 정중앙에 배치될 정사각형의 뷰 만들기
    UIView *oneView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
    oneView.translatesAutoresizingMaskIntoConstraints = NO;     //필수!
    [oneView setBackgroundColor:[UIColor brownColor]];
    [self.view addSubview:oneView];
    
    
  
    //*설명
    //constraintWithItem : 제약을 걸 객체
    //attribute : 제약 속성
    //relatedBy : 상대 객체와의 관계
    //toItem: 상대 뷰객체
    //attribute상대뷰 객체의 제약 속성

      //가로 길이 제약 조건
    NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:oneView
                                                                       attribute:NSLayoutAttributeWidth
                                                                       relatedBy:NSLayoutRelationEqual
                                                                          toItem:nil
                                                                       attribute:NSLayoutAttributeNotAnAttribute
                                                                      multiplier:1.f
                                                                        constant:50];
    //높이 제약 조건
    NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:oneView
                                                                        attribute:NSLayoutAttributeHeight
                                                                        relatedBy:NSLayoutRelationEqual
                                                                           toItem:nil
                                                                        attribute:NSLayoutAttributeNotAnAttribute
                                                                       multiplier:1.0
                                                                         constant:50];
    
    
    
    //oneView를 view 기준으로 view의 x 값 중앙에 위치.
    NSLayoutConstraint *centerX = [NSLayoutConstraint constraintWithItem:oneView
                                                               attribute:NSLayoutAttributeCenterX
                                                               relatedBy:NSLayoutRelationEqual
                                                                  toItem:self.view
                                                               attribute:NSLayoutAttributeCenterX
                                                              multiplier:1.0
                                                                constant:0];
    
    
    
    //oneView를 view 기준으로 view의 y 값 중앙에 위치.
    NSLayoutConstraint *centerY = [NSLayoutConstraint constraintWithItem:oneView
                                                               attribute:NSLayoutAttributeCenterY
                                                               relatedBy:NSLayoutRelationEqual
                                                                  toItem:self.view
                                                               attribute:NSLayoutAttributeCenterY
                                                              multiplier:1.0
                                                                constant:0];

    //크기 제약 조건 추가하기
    [oneView addConstraints:[NSArray arrayWithObjects:widthConstraint,heightConstraint,nil]];
    //위치 제약 조건 추가
    [self.view addConstraints:[NSArray arrayWithObjects:centerX,centerY,nil]];
    
    
    /*------------------------------------------------------------------------------------------------------------------*/
    
    //크기 유동 뷰 :  화면이 가로일때, 세로 일때 각각 다르게 변한다.
    UIView *twoView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
    twoView.translatesAutoresizingMaskIntoConstraints = NO;
    [twoView setBackgroundColor:[UIColor yellowColor]];
    [self.view addSubview:twoView];
    
    
    //높이 50고정 : 높이는 변하지 않게 고정 ,  가로는 설정안해준다. 설정해주면 화면을 가로로했을때 가로길이가 바뀌지 않기 때문이다.
    NSLayoutConstraint *heightCons = [NSLayoutConstraint constraintWithItem:twoView
                                                                  attribute:NSLayoutAttributeHeight
                                                                  relatedBy:NSLayoutRelationEqual
                                                                     toItem:nil
                                                                  attribute:NSLayoutAttributeNotAnAttribute
                                                                 multiplier:1.0
                                                                   constant:50];
    
    //twoView의 y 값은 view 중앙 y값
    NSLayoutConstraint *y = [NSLayoutConstraint constraintWithItem:twoView
                                                               attribute:NSLayoutAttributeTop
                                                               relatedBy:NSLayoutRelationEqual
                                                                  toItem:self.view
                                                               attribute:NSLayoutAttributeTop
                                                              multiplier:1.0
                                                                constant:30];
    
    
    
    
    //twoView의 왼쪽값은 view의 왼쪽에서 30 떨어지게
    NSLayoutConstraint *left = [NSLayoutConstraint constraintWithItem:twoView
                                                            attribute:NSLayoutAttributeLeading
                                                            relatedBy:NSLayoutRelationEqual
                                                               toItem:self.view
                                                            attribute:NSLayoutAttributeLeading
                                                           multiplier:1.0
                                                             constant:30];
    
     //twoView의 오른쪽값은 view의 오른쪽에서 30 떨어지게 (오른쪽은 self.view가 기준이 된다!!)
    NSLayoutConstraint *right = [NSLayoutConstraint constraintWithItem:self.view
                                                             attribute:NSLayoutAttributeTrailing
                                                             relatedBy:NSLayoutRelationEqual
                                                                toItem:twoView
                                                             attribute:NSLayoutAttributeTrailing
                                                            multiplier:1.0
                                                              constant:30];
    
    //높이 제약 조건 넣어주기.
    [twoView addConstraints:[NSArray arrayWithObjects:heightCons,nil]];
    //view에 y좌표, 왼쪽, 오른쪽 제약조건 넣어주기.
    [self.view addConstraints:[NSArray arrayWithObjects:y,left,right,nil]];
    
    
}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}


@end

 

예제파일

AutoLayoutSampleCode.zip
0.06MB

ios 지도 mkmap 2



위도와 경로 좌표를 이용해서 해당 주소 데이터를 찾는 방법을 알아본다. 먼저 앞의 포스팅에는 주소를 이용해서  미국 캘리포니아에 있는 애플의 위도와 경도를 구했다. 그 데이터를 이용해서 이번엔 주소를 찾아본다. 코드는 아래와 같다. 간단하다.


            //역방향 지오코딩

            

            let geoCoder = CLGeocoder()

            //cllocatoin객체는 위도와 경로 좌표로 초기화

            let newLocation = CLLocation(latitude: 37.3316833, longitude: -122.0301031)

            

            //geoCoder에 reverseGeocodeLocation 메서드로 전달 된다.

            geoCoder.reverseGeocodeLocation(newLocation, completionHandler: { (placemarks, error) in

                if error != nil {

                    print("에러 발생 \(error!.localizedDescription)")

                }

                //값이 있으면 배열 값으로 반환

                if placemarks!.count > 0 {

                    let placemark = placemarks![0]

                    //딕셔너리 값으로 반환

                    let addressDictionary = placemark.addressDictionary

                    

                    //key 값을 이용해서 주소 찾기

                    let address = addressDictionary!["Street"]

                    let city = addressDictionary!["City"]

                    let state = addressDictionary!["State"]

                    let zip = addressDictionary!["ZIP"]

                    

                    print("\(address!) \(city!) \(state!) \(zip!)")

                }            

})


결과 값은 아래와 같다.

1 Infinite Loop 쿠퍼티노 CA 95014


참고!

실제로 지오코딩은 ios 디바이스에서 실행되지 않고 변환이 필요할 때 마다 디바이스가 연결된 서버에서 실행이 된다.

변환이 끝나면 그 결과가 순차적으로 반환된다. 그래서 지오코딩은 인터넷에 연결되어 있을 때만 장소를 가져올 수 있다.




ios 지도 mkmap 1



텍스트 기반의 주소를 그 주소에 해당하는 좌표를 가지는 CLLocation 객체로 변환하는 CLGeocoder 클래스를 이용해서 미국 캘리포니아의 애플사의  좌표를 가져오는 예제를 살펴본다.




        let addressString = "One Infinite Loop, Cupertino, CA 95014"

        CLGeocoder().geocodeAddressString(addressString) { (placemarks, error) in

            

            if error != nil{

                print("에러발생 \(error!.localizedDescription)")

            }else if placemarks!.count > 0{

                let placemark = placemarks![0]

                let location = placemark.location

                let coords = location!.coordinate

                

                print("위도 : \(coords.latitude)")

                print("경도 : \(coords.longitude)")

                

            }



위의 코드를 실행하면 

위도 : 37.3316833

경도 : -122.0301031


각종 변수들을 찍어보면

    




            

placemark : 1 Infinite Loop, 1 Infinite Loop, 쿠쿠\355\215퍼티쿠\355\215퍼티\353\205노, CA  95014, 미 합중국 @ <+37.33168330,-122.03010310> +/- 100.00m, region CLCircularRegion (identifier:'<+37.33169175,-122.03021900> radius 62.40', center:<+37.33169175,-122.03021900>, radius:62.40m)

                




location : Optional(<+37.33168330,-122.03010310> +/- 100.00m (speed -1.00 mps / course -1.00) @ 16/05/2018, 15:05:16 Korean Standard Time)

                




coords : CLLocationCoordinate2D(latitude: 37.331683300000002, longitude: -122.03010310000001)







이같은 결과가 나온다.


이 코드는 간단하게 주소를 담은 문자열을 전달해서 CLGeocoder 인스턴스의 geocodeAddressString 메서드를 호출하고 변환이 끝나면 완료 핸들러를 호출 한다.

위치좌표 개수는 placemarks 배열 에 담아 반환되는데 하나 이상이면 위치좌표를 얻었다. 위의 코드에서는 위치좌표가 한 개라고 가정하고 배열 0 번째 방에 데이터를 가져와서 뿌려줬다. 이러한 방식을 어려운 말로 순방향 지오코딩이라고 한다.  실제로 위에서 얻은 좌표를 확인해 보니 캘리포니아에 있는 애플 회사가 나왔다.













ios 실전 앱 만들기 : 어떤 앱이 만들어 질까



logincontroller.swift


//  Copyright © 2018년 MacBookPro. All rights reserved.

// t\색상68cd4c


import UIKit

import Firebase

class LoginController: UIViewController,UIPickerViewDelegate,UIPickerViewDataSource {

    

    //사진 선택했는지 체크

    var picCheck = false

    

    //피커뷰 데이터

    let gender = ["남자","여자"]

    var age:[String] = []

    

    var messagesController: MessageController?

    

    //피커뷰 객체

    let genderPickerView :UIPickerView = {

        let pick = UIPickerView()

        pick.tag = 1

        return pick

    }()

    //나이 피커뷰 객체

    let agePickerView :UIPickerView = {

        let pick = UIPickerView()

        pick.tag = 2

        return pick

    }()

    

    //피커뷰 상속 메서드

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

        return 1

    }

    //피커뷰 row 개수

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


        var countrows : Int = age.count

        if (pickerView == genderPickerView){

            countrows = gender.count

        }

        return countrows

    }

    

    //피커뷰 제목

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

        if pickerView == genderPickerView {

            let titleRow = gender[row]

            return titleRow

        } else if pickerView == agePickerView {

            let titleRow = age[row]

            return titleRow

        }

        return ""

    }

    //피커뷰를 선택했을 때

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

        if pickerView == genderPickerView {

            let titleRow = gender[row]

            genderSegmentControl.text = titleRow

            genderSegmentControl.resignFirstResponder()

        } else if pickerView == agePickerView {

            let titleRow = age[row]

            ageTextField.text = titleRow

            ageTextField.resignFirstResponder()

        }

    }

    

    //컨테이너 뷰

    let inputsContainerView: UIView = {

        let view = UIView()

        view.backgroundColor = UIColor.black

        view.translatesAutoresizingMaskIntoConstraints = false

        view.layer.cornerRadius = 5

        view.layer.masksToBounds = true

        return view

    }()

    

    //로그인 버튼

    lazy var loginRegisterButton: UIButton = {

        let button = UIButton(type: .system)

        button.backgroundColor = UIColor(red:0.41, green:0.80, blue:0.30, alpha:1.0)

        button.setTitle("가입ㄱㄱ", for: UIControlState())

        button.translatesAutoresizingMaskIntoConstraints = false

        button.setTitleColor(.black, for: UIControlState())

        button.titleLabel?.font = UIFont(name: "SDMiSaeng", size: 30)

        button.layer.cornerRadius = 5

        button.layer.masksToBounds = true

        button.addTarget(self, action: #selector(handleLoginRegister), for: .touchUpInside)

        return button

    }()

    

    @objc func handleLoginRegister(){

        if loginRegisterSegmentedControl.selectedSegmentIndex == 0 {

            handleLogin()

        }else{

            handleRegister()

        }

    }

    

    //로그인 버튼 액션

    func handleLogin(){

        guard let email = emailTextField.text, let password = passwordTextField.text else {

            print("값이 없거나 잘못된 형식")

            return

        }

        

        Auth.auth().signIn(withEmail: email, password: password) { (user, error) in

            if let error = error {

                print(error)

                return

            }

            //로그인 할때 네비게이션 바 데이터 변경 함수 호출

            self.messagesController?.fetchUserAndSetupNavBarTitle()

            //로그인 성공시 로그인창 내려주기

            self.dismiss(animated: true, completion: nil)

        }

        

    }

    

    //닉네임 텍스트 필드

    let nameTextField: UITextField = {

        let tf = UITextField()

        tf.placeholder = "어렸을때 별명ㅋㅋ"

        if let placeholder = tf.placeholder {

            tf.attributedPlaceholder = NSAttributedString(string:placeholder,

                                                                     attributes: [NSAttributedStringKey.foregroundColor: UIColor(red:0.41, green:0.80, blue:0.30, alpha:1.0)])

        }

        tf.font = UIFont(name: "SDMiSaeng", size: 25)

        tf.textColor = UIColor(red:0.41, green:0.80, blue:0.30, alpha:1.0)

        tf.tintColor = UIColor(red:0.41, green:0.80, blue:0.30, alpha:1.0)

        tf.translatesAutoresizingMaskIntoConstraints = false

        return tf

    }()

    //닉네임 구분선

    let nameSeparatorView: UIView = {

        let view = UIView()

        view.backgroundColor = UIColor(red:0.41, green:0.80, blue:0.30, alpha:1.0)

        view.translatesAutoresizingMaskIntoConstraints = false

        return view

    }()

    

    //이메일 텍스트 필드

    let emailTextField: UITextField = {

        let tf = UITextField()

        tf.placeholder = "비번 찾을때 이메일"

        if let placeholder = tf.placeholder {

            tf.attributedPlaceholder = NSAttributedString(string:placeholder,

                                                          attributes: [NSAttributedStringKey.foregroundColor: UIColor(red:0.41, green:0.80, blue:0.30, alpha:1.0)])

        }

        tf.font = UIFont(name: "SDMiSaeng", size: 25)

        tf.textColor = UIColor(red:0.41, green:0.80, blue:0.30, alpha:1.0)

        tf.tintColor = UIColor(red:0.41, green:0.80, blue:0.30, alpha:1.0)

        tf.translatesAutoresizingMaskIntoConstraints = false

        return tf

    }()

    //이메일 구분선

    let emailSeparatorView: UIView = {

        let view = UIView()

        view.backgroundColor = UIColor(red:0.41, green:0.80, blue:0.30, alpha:1.0)

        view.translatesAutoresizingMaskIntoConstraints = false

        return view

    }()

    //패스워드 텍스트 필드

    let passwordTextField: UITextField = {

        let tf = UITextField()

        tf.placeholder = "비밀번호는 여섯자리 이상"

        if let placeholder = tf.placeholder {

            tf.attributedPlaceholder = NSAttributedString(string:placeholder,

                                                          attributes: [NSAttributedStringKey.foregroundColor: UIColor(red:0.41, green:0.80, blue:0.30, alpha:1.0)])

        }

        tf.font = UIFont(name: "SDMiSaeng", size: 25)

        tf.textColor = UIColor(red:0.41, green:0.80, blue:0.30, alpha:1.0)

        tf.tintColor = UIColor(red:0.41, green:0.80, blue:0.30, alpha:1.0)

        tf.translatesAutoresizingMaskIntoConstraints = false

        tf.isSecureTextEntry = true

        return tf

    }()

    

    //패스워드 구분선

    let passwordSeparatorView: UIView = {

        let view = UIView()

        view.backgroundColor = UIColor(red:0.41, green:0.80, blue:0.30, alpha:1.0)

        view.translatesAutoresizingMaskIntoConstraints = false

        return view

    }()

    

    //성별

    lazy var genderSegmentControl: UITextField = {

        let tf = UITextField()

        tf.placeholder = "남자 여자?"

        if let placeholder = tf.placeholder {

            tf.attributedPlaceholder = NSAttributedString(string:placeholder,

                                                          attributes: [NSAttributedStringKey.foregroundColor: UIColor(red:0.41, green:0.80, blue:0.30, alpha:1.0)])

        }

        tf.font = UIFont(name: "SDMiSaeng", size: 25)

        tf.textColor = UIColor(red:0.41, green:0.80, blue:0.30, alpha:1.0)

        tf.tintColor = UIColor(red:0.41, green:0.80, blue:0.30, alpha:1.0)

        tf.translatesAutoresizingMaskIntoConstraints = false

        return tf

        

    }()

    

    //성별 구분선

    let genderSeperatorView:UIView = {

        let view = UIView()

        view.backgroundColor = UIColor(red:0.41, green:0.80, blue:0.30, alpha:1.0)

        view.translatesAutoresizingMaskIntoConstraints = false

        return view

    }()

    

    

    //나이

    lazy var ageTextField:UITextField = {

        let tf = UITextField()

        tf.placeholder = "나이"

        if let placeholder = tf.placeholder {

            tf.attributedPlaceholder = NSAttributedString(string:placeholder,

                                                          attributes: [NSAttributedStringKey.foregroundColor: UIColor(red:0.41, green:0.80, blue:0.30, alpha:1.0)])

        }

        tf.font = UIFont(name: "SDMiSaeng", size: 25)

        tf.textColor = UIColor(red:0.41, green:0.80, blue:0.30, alpha:1.0)

        tf.tintColor = UIColor(red:0.41, green:0.80, blue:0.30, alpha:1.0)

        tf.translatesAutoresizingMaskIntoConstraints = false

        return tf

    }()

    

    

    //프로필 이미지

    lazy var profileImageView: UIImageView = {

        let imageView = UIImageView()

        imageView.image = UIImage(named: "main2")

        imageView.translatesAutoresizingMaskIntoConstraints = false

        imageView.contentMode = .scaleAspectFill

        //클릭시 이벤트 등록

        imageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleSelectProfileImageView)))

        imageView.isUserInteractionEnabled = true

        return imageView

    }()

    

    lazy var loginRegisterSegmentedControl:UISegmentedControl = {

        

        let sc = UISegmentedControl(items: ["로그인","가입ㄱㄱ"])

        sc.translatesAutoresizingMaskIntoConstraints = false

        let font = UIFont(name: "SDMiSaeng", size: 20)

        sc.setTitleTextAttributes([NSAttributedStringKey.font: font as Any],

                                                for: .normal)

        sc.tintColor = UIColor(red:0.41, green:0.80, blue:0.30, alpha:1.0)

        sc.selectedSegmentIndex = 1

        sc.addTarget(self, action: #selector(handleLoginRegisterChange), for: .valueChanged)

        return sc

    }()

    

    //로그인 등록 세그먼트

    @objc func handleLoginRegisterChange(){

        let title = loginRegisterSegmentedControl.titleForSegment(at: loginRegisterSegmentedControl.selectedSegmentIndex)

        loginRegisterButton.setTitle(title, for: UIControlState())

        

        let containerHeight =  loginRegisterSegmentedControl.selectedSegmentIndex  == 0 ? 100 : 200

        inputsContainerViewHeightAnchor?.constant = CGFloat(containerHeight)

        

        //이름 필드 높이

        nameTextFieldHeightAnchor?.isActive = false

        nameTextFieldHeightAnchor = loginRegisterSegmentedControl.selectedSegmentIndex  == 0 ? nameTextField.heightAnchor.constraint(equalTo: inputsContainerView.heightAnchor, multiplier: 0) : nameTextField.heightAnchor.constraint(equalTo: inputsContainerView.heightAnchor, multiplier: 1/5)

        nameTextFieldHeightAnchor?.isActive = true

        

        //이메일 필드 높이

        emailTextFieldHeightAnchor?.isActive = false

        emailTextFieldHeightAnchor = loginRegisterSegmentedControl.selectedSegmentIndex  == 0 ? emailTextField.heightAnchor.constraint(equalTo: inputsContainerView.heightAnchor, multiplier: 1/2) : emailTextField.heightAnchor.constraint(equalTo: inputsContainerView.heightAnchor, multiplier: 1/5)

        emailTextFieldHeightAnchor?.isActive = true

        

        //비밀번호 필드 높이

        passwordTextFieldHeightAnchor?.isActive = false

        passwordTextFieldHeightAnchor = loginRegisterSegmentedControl.selectedSegmentIndex  == 0 ? passwordTextField.heightAnchor.constraint(equalTo: inputsContainerView.heightAnchor, multiplier: 1/2) : passwordTextField.heightAnchor.constraint(equalTo: inputsContainerView.heightAnchor, multiplier: 1/5)

        passwordTextFieldHeightAnchor?.isActive = true

        

        //성별 높이

        genderSegmentControlHeightAnchor?.isActive = false

        genderSegmentControlHeightAnchor = loginRegisterSegmentedControl.selectedSegmentIndex  == 0 ? genderSegmentControl.heightAnchor.constraint(equalTo: inputsContainerView.heightAnchor, multiplier: 0) : genderSegmentControl.heightAnchor.constraint(equalTo: inputsContainerView.heightAnchor, multiplier: 1/5)

        genderSegmentControlHeightAnchor?.isActive = true

        

        //나이 높이

        ageTextFieldHeightAnchor?.isActive = false

        ageTextFieldHeightAnchor = loginRegisterSegmentedControl.selectedSegmentIndex  == 0 ? ageTextField.heightAnchor.constraint(equalTo: inputsContainerView.heightAnchor, multiplier: 0) : ageTextField.heightAnchor.constraint(equalTo: inputsContainerView.heightAnchor, multiplier: 1/5)

        ageTextFieldHeightAnchor?.isActive = true

        

    }

    

    

    

    //진입점

    override func viewDidLoad() {

        super.viewDidLoad()

        

        //나이 피커뷰에 데이터 넣어주기

        for i in 8...99{

            self.age.append("\(i)")

        }

        

        //바깥탭 -> 키보드 숨기기

        self.hideKeyboard()

        

        //피커뷰 델리게이트

        genderPickerView.delegate = self

        genderPickerView.dataSource = self

        agePickerView.delegate = self

        agePickerView.dataSource = self

        

        view.backgroundColor = .black

        

        view.addSubview(inputsContainerView)

        view.addSubview(loginRegisterButton)

        view.addSubview(profileImageView)

        view.addSubview(loginRegisterSegmentedControl)

        

        

        //입력 객체

        setupInputsContainerView()

        //로그인버튼 객체

        setupLoginRegisterButton()

        //이미지뷰 객체

        setupProfileImageView()

        //로그인 세그먼트 컨트롤

        setupLoginRegisterSegmentedControl()

        

    }

    

    //로그인등록 버튼 세그먼트 제약조건

    func setupLoginRegisterSegmentedControl(){

        loginRegisterSegmentedControl.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true

        loginRegisterSegmentedControl.bottomAnchor.constraint(equalTo: inputsContainerView.topAnchor, constant: -15).isActive = true

        loginRegisterSegmentedControl.heightAnchor.constraint(equalToConstant: 30).isActive = true

        loginRegisterSegmentedControl.widthAnchor.constraint(equalTo: inputsContainerView.widthAnchor).isActive = true

        

    }

    

    //높이 제약조건

    var inputsContainerViewHeightAnchor: NSLayoutConstraint?

    var nameTextFieldHeightAnchor: NSLayoutConstraint?

    var emailTextFieldHeightAnchor: NSLayoutConstraint?

    var passwordTextFieldHeightAnchor: NSLayoutConstraint?

    var genderSegmentControlHeightAnchor: NSLayoutConstraint?

    var ageTextFieldHeightAnchor: NSLayoutConstraint?

    

    //컨테이너 뷰 제약조건 설정

    func setupInputsContainerView(){

        //컨테이너 뷰 제약조건

        inputsContainerView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true

        inputsContainerView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true

        inputsContainerView.widthAnchor.constraint(equalTo:view.widthAnchor,constant:-24).isActive = true

        

        inputsContainerViewHeightAnchor = inputsContainerView.heightAnchor.constraint(equalToConstant: 200)

        inputsContainerViewHeightAnchor?.isActive = true

        

        

        //컨테이너 뷰 안에 뷰객체 넣기

        inputsContainerView.addSubview(nameTextField)

        inputsContainerView.addSubview(nameSeparatorView)

        inputsContainerView.addSubview(emailTextField)

        inputsContainerView.addSubview(emailSeparatorView)

        inputsContainerView.addSubview(passwordTextField)

        

        inputsContainerView.addSubview(passwordSeparatorView)

        inputsContainerView.addSubview(genderSegmentControl)

        genderSegmentControl.inputView = genderPickerView

        inputsContainerView.addSubview(genderSeperatorView)

        inputsContainerView.addSubview(ageTextField)

        ageTextField.inputView = agePickerView

        

        //뷰객체 제약조건 설정

        //이름

        nameTextField.leftAnchor.constraint(equalTo: inputsContainerView.leftAnchor, constant: 12).isActive = true

        nameTextField.topAnchor.constraint(equalTo: inputsContainerView.topAnchor, constant: 0).isActive = true

        nameTextField.widthAnchor.constraint(equalTo: inputsContainerView.widthAnchor, constant: 0).isActive = true

        

        nameTextFieldHeightAnchor = nameTextField.heightAnchor.constraint(equalTo: inputsContainerView.heightAnchor, multiplier: 1/5)

        nameTextFieldHeightAnchor?.isActive = true

        

        //이름 구분선

        nameSeparatorView.leftAnchor.constraint(equalTo: inputsContainerView.leftAnchor).isActive = true

        nameSeparatorView.topAnchor.constraint(equalTo: nameTextField.bottomAnchor).isActive = true

        nameSeparatorView.widthAnchor.constraint(equalTo: inputsContainerView.widthAnchor).isActive = true

        nameSeparatorView.heightAnchor.constraint(equalToConstant: 1).isActive = true

        

        //이메일

        emailTextField.leftAnchor.constraint(equalTo: inputsContainerView.leftAnchor, constant: 12).isActive = true

        emailTextField.topAnchor.constraint(equalTo: nameTextField.bottomAnchor, constant: 0).isActive = true

        emailTextField.widthAnchor.constraint(equalTo: inputsContainerView.widthAnchor, constant: 0).isActive = true

        emailTextFieldHeightAnchor = emailTextField.heightAnchor.constraint(equalTo: inputsContainerView.heightAnchor, multiplier: 1/5)

        emailTextFieldHeightAnchor?.isActive = true

        

        //이메일 구분선

        emailSeparatorView.leftAnchor.constraint(equalTo: inputsContainerView.leftAnchor).isActive = true

        emailSeparatorView.topAnchor.constraint(equalTo: emailTextField.bottomAnchor).isActive = true

        emailSeparatorView.widthAnchor.constraint(equalTo: inputsContainerView.widthAnchor).isActive = true

        emailSeparatorView.heightAnchor.constraint(equalToConstant: 1).isActive = true

        

        //비밀번호

        passwordTextField.leftAnchor.constraint(equalTo: inputsContainerView.leftAnchor, constant: 12).isActive = true

        passwordTextField.topAnchor.constraint(equalTo: emailTextField.bottomAnchor, constant: 0).isActive = true

        passwordTextField.widthAnchor.constraint(equalTo: inputsContainerView.widthAnchor, constant: 0).isActive = true

        passwordTextFieldHeightAnchor = passwordTextField.heightAnchor.constraint(equalTo: inputsContainerView.heightAnchor, multiplier: 1/5)

        passwordTextFieldHeightAnchor?.isActive = true

        

        //비밀번호 구분선

        passwordSeparatorView.leftAnchor.constraint(equalTo: inputsContainerView.leftAnchor).isActive = true

        passwordSeparatorView.topAnchor.constraint(equalTo: passwordTextField.bottomAnchor).isActive = true

        passwordSeparatorView.widthAnchor.constraint(equalTo: inputsContainerView.widthAnchor).isActive = true

        passwordSeparatorView.heightAnchor.constraint(equalToConstant: 1).isActive = true

        

        //성별

        genderSegmentControl.leftAnchor.constraint(equalTo: inputsContainerView.leftAnchor, constant: 12).isActive = true

        genderSegmentControl.topAnchor.constraint(equalTo: passwordTextField.bottomAnchor, constant: 0).isActive = true

        genderSegmentControl.widthAnchor.constraint(equalTo: inputsContainerView.widthAnchor, constant: 0).isActive = true

        genderSegmentControlHeightAnchor = genderSegmentControl.heightAnchor.constraint(equalTo: inputsContainerView.heightAnchor, multiplier: 1/5)

        genderSegmentControlHeightAnchor?.isActive = true

        

        //성별 구분선

        genderSeperatorView.leftAnchor.constraint(equalTo: inputsContainerView.leftAnchor).isActive = true

        genderSeperatorView.topAnchor.constraint(equalTo: genderSegmentControl.bottomAnchor).isActive = true

        genderSeperatorView.widthAnchor.constraint(equalTo: inputsContainerView.widthAnchor).isActive = true

        genderSeperatorView.heightAnchor.constraint(equalToConstant: 1).isActive = true

        

        //나이

        ageTextField.leftAnchor.constraint(equalTo: inputsContainerView.leftAnchor, constant: 12).isActive = true

        ageTextField.topAnchor.constraint(equalTo: genderSegmentControl.bottomAnchor, constant: 0).isActive = true

        ageTextField.widthAnchor.constraint(equalTo: inputsContainerView.widthAnchor, constant: 0).isActive = true

        ageTextFieldHeightAnchor = ageTextField.heightAnchor.constraint(equalTo: inputsContainerView.heightAnchor, multiplier: 1/5)

        ageTextFieldHeightAnchor?.isActive = true

        

    }

    

    //로그인버튼 제약 조건 설정

    func setupLoginRegisterButton(){

        loginRegisterButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true

        loginRegisterButton.topAnchor.constraint(equalTo: inputsContainerView.bottomAnchor, constant: 12).isActive = true

        loginRegisterButton.widthAnchor.constraint(equalTo: inputsContainerView.widthAnchor).isActive = true

        loginRegisterButton.heightAnchor.constraint(equalToConstant: 30).isActive = true

    }

    

    //프로필 이미지 제약 조건 설정

    func setupProfileImageView(){

        profileImageView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true

        profileImageView.bottomAnchor.constraint(equalTo: loginRegisterSegmentedControl.topAnchor, constant: -75).isActive = true

        profileImageView.widthAnchor.constraint(equalToConstant: 60).isActive = true

        profileImageView.heightAnchor.constraint(equalToConstant: 60).isActive = true

    }

    

    

}


extension UIColor {

    

    convenience init(r: CGFloat, g: CGFloat, b: CGFloat) {

        self.init(red: r/255, green: g/255, blue: b/255, alpha: 1)

    }

}


//바탕화면 텝 했을 때 키보드 숨기기

extension UIViewController

{

    func hideKeyboard()

    {

        let tap: UITapGestureRecognizer = UITapGestureRecognizer(

            target: self,

            action: #selector(UIViewController.dismissKeyboard))

        

        view.addGestureRecognizer(tap)

    }

    

    @objc func dismissKeyboard()

    {

        view.endEditing(true)

    }

}


LoginController+handlers.swift



//  Copyright © 2018년 MacBookPro. All rights reserved.

//


import UIKit

import Firebase


extension LoginController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {

    

    //등록 버튼 액션

    func handleRegister(){

        

        if(picCheck){

            //유효성 검사

            guard let email = emailTextField.text, let password = passwordTextField.text, let name = nameTextField.text,let gender = genderSegmentControl.text, let age = ageTextField.text else {

                

                

                //유효성 검사 해주기

                print("값이 없거나 잘못된 형식")

                return

            }

            //파이어베이스 가입

            Auth.auth().createUser(withEmail: email, password: password) { (user, error) in

                //에러 발생

                if let error = error{

                    if let errCode = AuthErrorCode(rawValue: error._code) {

                        switch errCode {

                        case .invalidEmail:

                            let alert = UIAlertController(title: "경고 ", message:"이메일 형식이 아닌것 같음..", preferredStyle: UIAlertControllerStyle.alert)

                            alert.addAction(UIAlertAction(title: "ㅇㅇ", style: UIAlertActionStyle.default, handler: nil))

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

                        case .wrongPassword :

                            let alert = UIAlertController(title: "경고 ", message:"비밀번호 확인 ㄱㄱ", preferredStyle: UIAlertControllerStyle.alert)

                            alert.addAction(UIAlertAction(title: "ㅇㅇ", style: UIAlertActionStyle.default, handler: nil))

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

                        case .accountExistsWithDifferentCredential :

                            let alert = UIAlertController(title: "경고 ", message:"이메일을 확인좀..", preferredStyle: UIAlertControllerStyle.alert)

                            alert.addAction(UIAlertAction(title: "ㅇㅇ", style: UIAlertActionStyle.default, handler: nil))

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

                        case .userNotFound :

                            let alert = UIAlertController(title: "경고 ", message:"존재하지 않는 이메일이여", preferredStyle: UIAlertControllerStyle.alert)

                            alert.addAction(UIAlertAction(title: "ㅇㅇ", style: UIAlertActionStyle.default, handler: nil))

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

                        default:

                            let alert = UIAlertController(title: "경고 ", message:"빈칸없는지 다시바바ㅠㅠ", preferredStyle: UIAlertControllerStyle.alert)

                            alert.addAction(UIAlertAction(title: "ㅇ", style: UIAlertActionStyle.default, handler: nil))

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

                        }

                    }

                    return

                }

                //가입후 uid  넘겨준다. uid 가 nil이면 return

                guard let uid = user?.uid else {

                    return

                }

                

                //유저 가입 성공후

                //유일한 스트링 값

                let imageName = NSUUID().uuidString

                //firebase 저장소 위치 가져오기

                let storageRef = Storage.storage().reference().child("profile_images").child("\(imageName).jpg")

                //image 파일 변환

                

                //더 안전하게

                if let profileImage = self.profileImageView.image, let uploadData = UIImageJPEGRepresentation(profileImage, 0.1){

                    

                    //업로드

                    storageRef.putData(uploadData, metadata: nil, completion: { (metadata, error) in

                        if let error = error {

                            print(error)

                            return

                        }

                        //이미지가 저장된 url 가져온 후 users db에 삽입

                        if let profileImageUrl = metadata?.downloadURL()?.absoluteString {

                            

                            let values = ["name": name, "email": email, "profileImageUrl": profileImageUrl,"gender":gender,"age":age]

                            //최종 저장 함수 호출(데이터 넘겨줌)

                            self.registerUserIntoDatabaseWithUID(uid, values: values as [String : AnyObject])

                        }

                    })

                }

            }

        }else{

            

            let alert = UIAlertController(title: "경고 ", message:"맨위에 바바 사진넣어야됨...", preferredStyle: UIAlertControllerStyle.alert)

            alert.addAction(UIAlertAction(title: "ㅇㅇ", style: UIAlertActionStyle.default, handler: nil))

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

        }

        

        

    }

    //최종적으로 유저 저장

    fileprivate func registerUserIntoDatabaseWithUID(_ uid: String, values: [String: AnyObject]) {

        let ref = Database.database().reference()

        let usersReference = ref.child("users").child(uid)

        //최종 저장

        usersReference.updateChildValues(values, withCompletionBlock: { (err, ref) in

            

            if let err = err {

                print(err)

                return

            }

            

            let user = User(dic:values)

            //등록 할때 메시지 컨트롤러에 있는 setupnavbar 함수 호출

            self.messagesController?.setupNavBarWithUser(user: user)

            self.dismiss(animated: true, completion: nil)

        })

        

        picCheck = false

    }

    

    

    //프로필 이미지 클릭했을 때 실행되는 이벤트 함수

    @objc func handleSelectProfileImageView(){

        

        let picker = UIImagePickerController()

        picker.delegate = self

        picker.allowsEditing = true

        

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

    }

    

    

    //이미지 피커 선택 완료

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {

        var selectedImageFromPicker: UIImage?

        

        if let editedImage = info["UIImagePickerControllerEditedImage"] as? UIImage {

            selectedImageFromPicker = editedImage

        } else if let originalImage = info["UIImagePickerControllerOriginalImage"] as? UIImage {

            

            selectedImageFromPicker = originalImage

        }

        

        if let selectedImage = selectedImageFromPicker {

            profileImageView.image = selectedImage

            picCheck = true

        }

        

        dismiss(animated: true, completion: nil)

    }

    

    

    //이미지 피커 취소

    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {

        dismiss(animated: true, completion: nil)

    }

    

}



mainviewcontroller.swift

//

//  MainViewController.swift

//  DatingApp

//

//  Created by MacBookPro on 2018. 5. 15..

//  Copyright © 2018년 MacBookPro. All rights reserved.

//


import UIKit

import Firebase

class MainViewController: UIViewController {


    var ref: DatabaseReference!

    var mainViewController: MainViewController?

    //진입점

    override func viewDidLoad() {

        super.viewDidLoad()

        

        navigationItem.leftBarButtonItem = UIBarButtonItem(title: "로그아웃", style: .plain, target:self , action: #selector(handleLogout))

        

        

        //로그인or로그아웃 체크

        checkIfUserIsLoggedIn()

        


        

    }


    //로그인or로그아웃 체크 함수

    func checkIfUserIsLoggedIn(){

        //로그아웃 되었을 때 실행

        if Auth.auth().currentUser?.uid == nil{

            perform(#selector(handleLogout), with: nil, afterDelay: 0)

        }else{

            

            //로그인 되었으면 네비게이션 타이틀의 제목을 유저 이름으로 지정해준다.

            fetchUserAndSetupNavBarTitle()

            

        }

    }

    

    func fetchUserAndSetupNavBarTitle(){

        //로그인 되었으면 네비게이션 타이틀의 제목을 유저 이름으로 지정해준다.

        guard let uid = Auth.auth().currentUser?.uid else{

            //uid가

            return

        }

        ref = Database.database().reference()

        ref.child("users").child(uid).observeSingleEvent(of: .value, with: { (snapshot) in

            print("타이틀 바 표시 함수 호출 성공-")

            print(snapshot)

            if let dictionary = snapshot.value as? [String: AnyObject]{

                

                let user = User(dic: dictionary)

                self.setupNavBarWithUser(user: user)

                

            }

        }, withCancel: nil)

        

    }

    

    //네비게이션 타이틀 바 변경해주기

    func setupNavBarWithUser(user: User){


        print("사진있는 타이틀! 호출")

        let titleView = MyUIView()

        

        titleView.frame = CGRect(x:0, y:0, width: 100, height: 50)

        

        //이미지와 라벨을 담을 뷰 객체

        let containerView = UIView()

        containerView.translatesAutoresizingMaskIntoConstraints = false

        

        titleView.addSubview(containerView)

        

        //이미지 객체

        let profileImageView = UIImageView()

        profileImageView.translatesAutoresizingMaskIntoConstraints = false

        profileImageView.contentMode = .scaleAspectFill

        profileImageView.layer.cornerRadius = 20

        profileImageView.clipsToBounds = true

        

        if let profileImageUrl = user.profileImageUrl{

            profileImageView.loadImageUsingCacheWithUrlString(profileImageUrl)

        }

        containerView.addSubview(profileImageView)

        

        profileImageView.leftAnchor.constraint(equalTo: containerView.leftAnchor).isActive = true

        profileImageView.centerYAnchor.constraint(equalTo: containerView.centerYAnchor).isActive = true

        profileImageView.widthAnchor.constraint(equalToConstant: 35).isActive = true

        profileImageView.heightAnchor.constraint(equalToConstant: 35).isActive = true

        

        let nameLable = UILabel()

        containerView.addSubview(nameLable)

        

        nameLable.text = user.name

        nameLable.translatesAutoresizingMaskIntoConstraints = false

        nameLable.leftAnchor.constraint(equalTo: profileImageView.rightAnchor, constant: 8).isActive = true

        nameLable.centerYAnchor.constraint(equalTo: profileImageView.centerYAnchor).isActive = true

        nameLable.rightAnchor.constraint(equalTo: containerView.rightAnchor).isActive = true

        nameLable.heightAnchor.constraint(equalTo: profileImageView.heightAnchor).isActive = true

        //nameLable.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(showChatController)))

        

        containerView.centerXAnchor.constraint(equalTo: titleView.centerXAnchor).isActive = true

        containerView.centerYAnchor.constraint(equalTo: titleView.centerYAnchor).isActive = true

        self.navigationItem.titleView = titleView

        

    }

    

    //로그아웃 액션

    @objc func handleLogout(){

        

        do{

            try Auth.auth().signOut()

        } catch let logError{

            print(logError)

        }

        

        mainViewController?.fetchUserAndSetupNavBarTitle()

        

        let loginController = LoginController()

        

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

    }

}



myuiview.swift

import UIKit


class MyUIView: UIView {

    

    override var intrinsicContentSize: CGSize {

        return UILayoutFittingExpandedSize

    }

    

}



Extensions.swift

//

//  Extensions.swift

//  DatingApp

//

// 

//  Copyright © 2018년 MacBookPro. All rights reserved.

//


import UIKit


let imageCache = NSCache<NSString, AnyObject>()


extension UIImageView{

    

    //새 메시지 보내기 테이블 뷰에서 호출하는 이미지 캐쉬 함수

    func loadImageUsingCacheWithUrlString(_ urlString: String) {

         self.image = nil

        

        //이미지 캐쉬가 있는지 확인 후 있으면 return

        if let cachedImage = imageCache.object(forKey: urlString as NSString) as? UIImage {

            self.image = cachedImage

            return

        }

        

        //이미지 캐치가 없으면 다운로드

        let url = URL(string: urlString)

        URLSession.shared.dataTask(with: url!, completionHandler: { (data, response, error) in

            

            //다운로드 중 에러발생, 종료

            if let error = error {

                print(error)

                return

            }

            

            DispatchQueue.main.async(execute: {

                //이미지 캐쉬에 넣기

                if let downloadedImage = UIImage(data: data!) {

                    imageCache.setObject(downloadedImage, forKey: urlString as NSString)

                    self.image = downloadedImage

                }

            })

            

        }).resume()

    }

    

}



//

//  User.swift

//  DatingApp

//

//  Copyright © 2018년 MacBookPro. All rights reserved.

//


import UIKit


class User: NSObject {

    

    var name : String?

    var email : String?

    var profileImageUrl: String?

    var id : String?

    init(dic:[String:Any]){

        self.name = dic["name"] as? String ?? ""

        self.email = dic["email"] as? String ?? ""

         self.profileImageUrl = dic["profileImageUrl"] as? String

        self.id = dic["id"] as? String

        

    }

    

}




'ios 뽀개기 > 실전' 카테고리의 다른 글

ios 실전 앱 만들기 : 어떤 앱이 만들어 질까  (0) 2018.05.15
instagram like  (0) 2018.03.06
alert 커스텀 슬라이더  (0) 2018.01.03
alert 커스텀 - 이미지  (0) 2018.01.03
alert 커스텀 - 지도 2  (0) 2018.01.03
alert 커스텀 - 지도1  (0) 2018.01.03



//찾아볼것

//1. 배열, 딕셔너리

//2. xml 파서

//3. guard


import UIKit


class ViewController: UIViewController,UITableViewDataSource,XMLParserDelegate {

    //배열에 딕셔너리를 넣었다.

    var datalist = [[String:String]]()

    var detaildata = [String:String]()

    var elementTemp:String = ""

    var blank = true

    override func viewDidLoad() {

        super.viewDidLoad()

       

        let urlString = "https://raw.githubusercontent.com/ChoiJinYoung/iphonewithswift2/master/weather.xml"

        

        //print(URL(string: urlString)!)

        

        guard let baseURL = URL(string: urlString) else {

            print("url error")

            return

        }

        

        guard let parser = XMLParser(contentsOf: baseURL) else {

            print("cant read data")

            return

        }

        

        parser.delegate = self

        if !parser.parse(){

            print("parser failure")

        }

    }

    /*

     

     //로컬부터 로컬까지 하나의 딕셔너리에 들어간다.

    didstartelement:weatherinfo

    foundCharacters:

    

    didstartelement:local

    foundCharacters:

    

    didstartelement:country

    foundCharacters:한국

     

    didEndElement:country

    foundCharacters:

    

    didstartelement:weather

    foundCharacters:비

    didEndElement:weather

    foundCharacters:

    

    didstartelement:temperature

    foundCharacters:20

    didEndElement:temperature

    foundCharacters:

    

    didEndElement:local

    foundCharacters:

*/


    //parser가 시작 태그를 만나면 호출된다.

    func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {

        //print("didstartelement:"+elementName)  //local

        

        elementTemp = elementName

        blank = true

        print("start:\(elementName)")

    }

    

    //현재 태그에 담겨있는 string값이 전달된다.

    func parser(_ parser: XMLParser, foundCharacters string: String) {

        //print("foundCharacters:"+string)

        

        if blank == true && elementTemp != "local" && elementTemp != "weatherinfo"{

            detaildata[elementTemp] = string.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)

        }

        

    }

    

    //parser가 닫는 태그를 만나면 호출된다.

    func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {

        //print("didEndElement:"+elementName)

        

        if elementName == "local"{

            datalist += [detaildata]

        }

        blank = false

        print("end:\(elementName)")

    }


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

        return datalist.count

    }


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

        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! WeatherViewCell

    

        var dicTemp = datalist[indexPath.row]

        

        cell.contryLable.text = dicTemp["country"]

        let weatherStr = dicTemp["weather"]

        

        

        cell.weatherLable.text = weatherStr

        cell.temLable.text = dicTemp["temperatur"]

        

        if weatherStr == "맑음" {

            cell.imgView.image = UIImage(named:"sunny.png")

        }else if weatherStr == "비" {

            cell.imgView.image = UIImage(named:"rainy.png")

        }else if weatherStr == "흐림" {

            cell.imgView.image = UIImage(named:"cloudy.png")

        }else if weatherStr == "눈" {

            cell.imgView.image = UIImage(named:"snow.png")

        }else {

            cell.imgView.image = UIImage(named:"blizzard.png")

        }

        

        return cell

    }

}






////  WeatherViewCell.swift

//

//  WeatherViewCell.swift

//  XML

//

//  Created by MacBookPro on 2017. 12. 18..

//  Copyright © 2017년 MacBookPro. All rights reserved.

//


import UIKit


class WeatherViewCell: UITableViewCell {

    @IBOutlet weak var contryLable: UILabel!

    @IBOutlet weak var weatherLable: UILabel!

    @IBOutlet weak var temLable: UILabel!

    @IBOutlet weak var imgView: UIImageView!

    

    override func awakeFromNib() {

        super.awakeFromNib()

        // Initialization code

    }


    override func setSelected(_ selected: Bool, animated: Bool) {

        super.setSelected(selected, animated: animated)


        // Configure the view for the selected state

    }


}





/*


//로컬부터 로컬까지 하나의 딕셔너리에 들어간다.

didstartelement:weatherinfo

foundCharacters:


didstartelement:local

foundCharacters:


didstartelement:country

foundCharacters:한국


didEndElement:country

foundCharacters:


didstartelement:weather

foundCharacters:비

didEndElement:weather

foundCharacters:


didstartelement:temperature

foundCharacters:20

didEndElement:temperature

foundCharacters:


didEndElement:local

foundCharacters:

*/

'ios 뽀개기 > ios 응용해보기' 카테고리의 다른 글

php에서 myql파일 json으로 가져오기  (0) 2017.12.19
json parser  (0) 2017.12.18
xml parser  (0) 2017.12.18
페이지 뷰  (0) 2017.12.15
탭뷰  (0) 2017.12.15
맵뷰 map view  (0) 2017.12.14

1 아이폰 ios 스위프트 계산기 만들기



//

//  ViewController.swift

//  Calulatoer2

//

//  Created by MacBookPro on 2017. 12. 5..

//  Copyright © 2017년 MacBookPro. All rights reserved.

//



//controller 부분


//모듈: 클래스의 집합

import UIKit

//상속받음 , controller는 uiviewcontroller의 상속을 받는다.

class ViewController: UIViewController {


    //라벨 아웃렛 변수

    @IBOutlet weak var display: UILabel! //!를 붙여주면 값을 암묵적으로 추출해서 누구든 자유롭게 쓸수 있게 하겠다는 뜻, 만약 nil이면 앱이 멈춘다.

    

    //스위프트에서는 모든 변수가 초기값을 가져야 한다!

    var userIsInTheMiddleOfTyping: Bool = false

    

    //숫자 눌렀을 때

    @IBAction func touchDigit(_ sender: UIButton) {

        

        // 경고가 뜬다.

        //let을 넣어야 한다. 읽어오고 싶을 때..array나 dictionary를 사용할 때

        //"!" 는 옵셔널 강제적으로 값을 가져올때, 하지만 set 되지 않은 값이 있다면 충돌이 일어난다.

        //안전하게 꺼내오는 방법이 있다.

        

        let digit = sender.currentTitle! //!를 붙여줘서 옵셔널 -> stirng 타입으로 바뀌었다.

        

        //숫자를 입력중이라면

        if(userIsInTheMiddleOfTyping){

            //text를 옵셔널 타입에 보낼수 없어서 생기는 에러(옵셔널은 텍스트를 이해할 수 없다.-> ullable 타입인 display의 연관값을 가져와야 한다.)

            //display의 값이 nil이라면 에러가 뜬다.

            let textCurrentlyInDisplay = display.text!

            display.text = textCurrentlyInDisplay + digit

        }else{

            //입력중이 아니라면(처음 키패드를 눌렀을 때 0이 나오게)

            display.text = digit

        }

        userIsInTheMiddleOfTyping = true


    }

}



//

//  ViewController.swift

//  Calulatoer2

//

//  Created by MacBookPro on 2017. 12. 5..

//  Copyright © 2017년 MacBookPro. All rights reserved.

//



//controller 부분


//모듈: 클래스의 집합

import UIKit

//상속받음 , controller는 uiviewcontroller의 상속을 받는다.

class ViewController: UIViewController {


    //라벨 아웃렛 변수

    @IBOutlet weak var display: UILabel! //!를 붙여주면 값을 암묵적으로 추출해서 누구든 자유롭게 쓸수 있게 하겠다는 뜻, 만약 nil이면 앱이 멈춘다.

    

    //스위프트에서는 모든 변수가 초기값을 가져야 한다!

    var userIsInTheMiddleOfTyping: Bool = false

    

    //숫자 눌렀을 때

    @IBAction func touchDigit(_ sender: UIButton) {

        

        // 경고가 뜬다.

        //let을 넣어야 한다. 읽어오고 싶을 때..array나 dictionary를 사용할 때

        //"!" 는 옵셔널 강제적으로 값을 가져올때, 하지만 set 되지 않은 값이 있다면 충돌이 일어난다.

        //안전하게 꺼내오는 방법이 있다.

        

        let digit = sender.currentTitle! //!를 붙여줘서 옵셔널 -> stirng 타입으로 바뀌었다.

        

        //숫자를 입력중이라면

        if(userIsInTheMiddleOfTyping){

            //text를 옵셔널 타입에 보낼수 없어서 생기는 에러(옵셔널은 텍스트를 이해할 수 없다.-> ullable 타입인 display의 연관값을 가져와야 한다.)

            //display의 값이 nil이라면 에러가 뜬다.

            let textCurrentlyInDisplay = display.text!

            display.text = textCurrentlyInDisplay + digit

        }else{

            //입력중이 아니라면(처음 키패드를 눌렀을 때 0이 나오게)

            display.text = digit

        }

        userIsInTheMiddleOfTyping = true


    }

    

    //파이가 눌러졌을때

    @IBAction func performOperation(_ sender: UIButton) {

    

        //뒤에 느낌표 붙여서 옵셔널을 스트링값으로 바꾸기 (강제추출) , 값이 nil일 때 충돌남

        //let mathematicalSymbol = sender.currentTitle!

        

        //더 안전한 방법(set이 되었을 때)

        if let mathematicalSymbol = sender.currentTitle{

            //파이를 입력할 때 숫자들 지워버리기

            userIsInTheMiddleOfTyping = false

            if mathematicalSymbol == "π"{

                display.text = String(M_PI)

            }

            

        }

        

    }

    

    

}




//

//  ViewController.swift

//  Audio

//

//  Created by MacBookPro on 2017. 11. 28..

//  Copyright © 2017년 MacBookPro. All rights reserved.

//

//오디오를 재생하려면 헤더 파일과 델리게이트가 필요하다.

import UIKit

import AVFoundation

class ViewController: UIViewController,AVAudioPlayerDelegate, AVAudioRecorderDelegate{

    

    

    //변수 및 상수

    var audioPlayer : AVAudioPlayer!    //avaudioplayer인스턴스 변수

    var audioFile : URL!                // 재생할 오디오의 파일명 변수

    let MAX_VOLUME : Float = 10.0       //최대 불륨, 실수형 상수

    var progressTimer : Timer!          //타이머를 위한 변수

    

    let timePlayerSelector:Selector = #selector(ViewController.updatePlayTime)

    let timeRecordSelector:Selector = #selector(ViewController.updateRecordTime)

    

    //재생 아웃렛 변수

    @IBOutlet weak var pvProgressPlay: UIProgressView!

    @IBOutlet weak var lbCurrentTime: UILabel!

    @IBOutlet weak var lbEndTime: UILabel!

    

    @IBOutlet weak var btnPlay: UIButton!

    @IBOutlet weak var btnPause: UIButton!

    @IBOutlet weak var btnStop: UIButton!

    @IBOutlet weak var slVolume: UISlider!

    

    

    

    //녹음 아웃렛 변수

    

    @IBOutlet weak var btnRecord: UIButton!

    @IBOutlet weak var lbRecordTime: UILabel!

    

    var audioReorder : AVAudioRecorder!

    var isRecorderMode = false //현재는 재생 모드

    

    

    override func viewDidLoad() {

        super.viewDidLoad()

        

        

        selectAudioFile()

        

        

        if (!isRecorderMode) {

            

            //오디오를 초기화 하는 함수

            initPlay()

            //녹음 버튼과 녹음 재생시간은 비활성화

            btnRecord.isEnabled = false

            lbRecordTime.isEnabled = false

        }else{

            

            initRecord()

            

        }

        

        

    }

    

    //녹음기능 초기화 함수

    func selectAudioFile() -> Void {

        

        //재생모드

        if !isRecorderMode {

                    audioFile = Bundle.main.url(forResource: "music", withExtension: "mp3")

        }else{//녹음모드

           //녹음모드일 때는 새 파일인 recordFile.m4a가 생성 된다.

           let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]

           audioFile = documentDirectory.appendingPathComponent("recordFile.m4a")

           

            

            

        }

    }

    //녹음을 위한 초기화 함수 : 음질은 최대, 비트율 320kbps, 오디오 채널은 2, 샘플율은 44,100hz

    func initRecord() -> Void {

        

        let recordSettings = [

            AVFormatIDKey : NSNumber(value : kAudioFormatAppleLossless as UInt32),

            AVEncoderAudioQualityKey : AVAudioQuality.max.rawValue,

            AVEncoderBitRateKey : 320000,

            AVNumberOfChannelsKey : 2,

            AVSampleRateKey : 44100.0] as [String : Any]

        

        do {

            // selectAudioFile 함수에서 저장한 audioFile을 url로 하는 audioRecorder 인스턴스를 생성

            audioReorder = try AVAudioRecorder(url: audioFile, settings: recordSettings)

        } catch let error as NSError {

            print("error-initRecord:\(error)")

        }

        audioReorder.delegate = self

        //박자관련

        audioReorder.isMeteringEnabled = true

        

        audioReorder.prepareToRecord()

        

        slVolume.value = 1.0

        audioPlayer.volume = slVolume.value

        lbEndTime.text = convertNSTimeInterval2String(0)

        lbCurrentTime.text = convertNSTimeInterval2String(0)

        //버튼 모드 비활성화

        setPlayButton(false, pause: false, stop: false)

        

        let session = AVAudioSession.sharedInstance()

        

        do {

            try session.setCategory(AVAudioSessionCategoryPlayAndRecord)

        } catch let error as NSError {

            print("error-setcategory : \(error)")

        }

        

        do {

            try session.setActive(true)

        } catch let error as NSError {

            print("error-setActive : \(error)")

        }

        

    }


    //audioFile을 url로 하는 audioplayer 인스턴스 생성

    // 입력파라미터인 오디오 파일이 없을 때에 대비하여 try catch문 사용한다.

    func initPlay(){

        

        //에러 처리

        do {

            audioPlayer = try AVAudioPlayer(contentsOf: audioFile) //오류발생 가능 함수

        } catch let error as NSError { //오류타입

            print("error-initplay : \(error)")  //오류타입에 대한 처리 구문

        }

        

        //슬라이더의 최대 불륨을 지정

        slVolume.maximumValue =  MAX_VOLUME

        //슬라이더의 불륨을 1.0으로 초기화

        slVolume.value = 1.0

        //프로그레스 뷰의 진행을 0으로 초기화

        pvProgressPlay.progress = 0

        

        //audioplayer의 델리게이트를 self로 한다.

        audioPlayer.delegate = self

        //prepareToplay 실행한다.

        audioPlayer.prepareToPlay()

        //오디오 플레이어의 블륨 초기화 한다.

        audioPlayer.volume = slVolume.value

        

        //오디오 파일의 재생시간인 audioplayer.duration값을 이 함수를 이용해서 텍스트에 출력

        lbEndTime.text = convertNSTimeInterval2String(audioPlayer.duration)

        //lbcurrentTime텍스트에는 이 함수를 이용해서 00:00이 출력되도록 한다.

        lbCurrentTime.text = convertNSTimeInterval2String(0)

        

        //play 버튼 활성화

        //btnPlay.isEnabled = true

        //btnPause.isEnabled = false

        //btnStop.isEnabled = false

        

        

        setPlayButton(true, pause: false, stop: false)

    }

    //

    func setPlayButton(_ play:Bool, pause:Bool, stop:Bool) -> Void {

        btnPlay.isEnabled = play

        btnPause.isEnabled = pause

        btnStop.isEnabled = stop

    }

    

    //00:00형태로 바꾸기 위해 timeinterval 값을 받아 문자열로 돌려보내는 함수

    func convertNSTimeInterval2String(_ time:TimeInterval) -> String {

        //재생시간의 매개변수인 time값을 60으로 나눈 몫을 정수 값으로 변환하여 상수 min에 초기화

        let min = Int(time/60)

        //time값을 60으로 나눈 나머지 값을 정수 값으로 변환하여 상수 sec 값에 초기화 한다.

        let sec = Int(time.truncatingRemainder(dividingBy: 60))

        //이 두 값을 이용해서 "%02d:%02d" 형태의 문자열로 변환하여 상수에 초기화

        let strTime = String(format: "%02d:%02d",min,sec)

        

        return strTime

        

    }

    

    

    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

        // Dispose of any resources that can be recreated.

    }


    //재생 버튼

    @IBAction func btnPlayAudio(_ sender: UIButton) {

        

        //오디오 재생

        audioPlayer.play()

        //재생버튼 비활성화 나머지 버튼 활성화

        setPlayButton(false, pause: true, stop: true)

        //프로그레스 타이머 설정

        progressTimer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: timePlayerSelector, userInfo: nil, repeats: true)

        

    }

    

    

    //앞에서 만든 타이머에 의해 0.1초 간격으로 이 함수가 실행되는데, 그때마다 재생시간을 라벨과 프로그래스바에 보여준다.

    @objc func updatePlayTime() -> Void {

        lbCurrentTime.text = convertNSTimeInterval2String(audioPlayer.currentTime)

        pvProgressPlay.progress = Float(audioPlayer.currentTime/audioPlayer.duration)

    }

    

    //일시정지버튼

    @IBAction func btnPauseAudio(_ sender: UIButton) {

        audioPlayer.pause()

        setPlayButton(true, pause: false, stop: true)

    }

    

    //정지버튼

    @IBAction func btnStopAudio(_ sender: UIButton) {

        audioPlayer.stop()

        

        //오디오를 정지하고 재생하면 다시 처음부터 재생, 시간 초기화

        audioPlayer.currentTime = 0

        lbCurrentTime.text = convertNSTimeInterval2String(0)

        setPlayButton(true, pause: false, stop: false)

        progressTimer.invalidate()

        

    }

    

    //음량 슬라이더 액션 함수

    @IBAction func slChangeVolume(_ sender: UISlider) {

        

        audioPlayer.volume = slVolume.value

    }

    

    //오디오 재생이 끝나면 맨 처음 상태로 돌아가도록 지정 하는 함수

    func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {

        progressTimer.invalidate()

        //재생버튼 활성화 나머지 버튼 비활성화

        setPlayButton(true, pause: false, stop: false)

    }

    

    

    //녹음 스위치

    @IBAction func swRecordMode(_ sender: UISwitch) {

        

        //스위치가 녹음 모드일 때

        if sender.isOn {

            

            //오디오 재생 중지, 현재시간 00:00으로 설정, record값 참으로 설정 녹음 버튼과 시간 활성화

            audioPlayer.stop()

            audioPlayer.currentTime = 0

            lbRecordTime!.text = convertNSTimeInterval2String(0)

            isRecorderMode = true

            btnRecord.isEnabled = true

            lbRecordTime.isEnabled = true

        }else{

            //재생모드일 때, 레코드모드 값을 거짓으로 바꾸고, 녹음 버튼과 시간을 비활성화, 녹음 시간 0

            

            isRecorderMode = false

            btnRecord.isEnabled = false

            lbRecordTime.isEnabled = false

            lbRecordTime.text = convertNSTimeInterval2String(0)

        }

        

        //이 함수를 호출해서 오디오 파일을 선택하고 , 모드에 따라서 초기화 할 함수 호출

        selectAudioFile()

        

        if !isRecorderMode {

            initPlay()

        }else {

        

            initRecord()

            

        }

        

    }

    //녹음하기 버튼 함수

    @IBAction func btnRecord(_ sender: UIButton) {

        

        if sender.titleLabel?.text == "record"{

            audioReorder.record()

            sender.setTitle("stop", for: UIControlState())

            progressTimer=Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: timeRecordSelector, userInfo: nil, repeats: true)

        }else {

            

            audioReorder.stop()

            progressTimer.invalidate() //녹음이 정지되면 타이머 무효화

            sender.setTitle("record", for: UIControlState())

            btnPlay.isEnabled = true

            initPlay()

        }

        

    }

    

    @objc func updateRecordTime(){

        lbRecordTime.text = convertNSTimeInterval2String(audioReorder.currentTime)

    }

}


  1. 한상민 2019.01.17 14:10

    개발자님 한가지 질문드리겠습니다
    스위프트 xcode 9 swift 4버젼에서 avplayer을 이용하여 서버에서 url 받고 이를 실행 시키고 싶은데
    "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3" 이런 형태의 오디오 url은 받아 오는데
    "https://s3carpum.s3.us-east-2.amazonaws.com/ce5e57608a0d5c335d5c0311096c8d0c067b11e0caaf730793a3919967fee20fd33f3383d41a5094930c7e88d45b877a.mp4" 이런 형태의 url은 재생을 못 시킵니다.
    안드로이드에서는 받아와서 재생이 되는데 스위프트에서는 재생이 안됩니다.
    첫번째 url은 어플 실행시키면 권한에 대해서 알림이 오는데 두번째 url 은 권한에 대해서 알림이 오지도 조차 않습니다
    혹시 스위프트에서 는 특정 오디오url 만 가능합니까?

    • 길위의 개발자 2019.01.17 14:27 신고

      안녕하세요. 첫번째 url은 작동이 되는데.. 아래 url 들어가보니 스트리밍이 안되는데 지금 서버가 꺼져있나요?

      https://www.s3carpum.s3.us-east-2.amazonaws.com/ce5e57608a0d5c335d5c0311096c8d0c067b11e0caaf730793a3919967fee20fd33f3383d41a5094930c7e88d45b877a.mp4

    • 길위의 개발자 2019.01.17 14:29 신고

      audioFile = Bundle.main.url(forResource: "music", withExtension: "mp3")
      서버가 작동중이라면, file 확장자도 한번 확인해주세요

  2. 한상민 2019.01.17 15:05

    빠른 답변 감사합니다!!
    서버는 항상 켜져 있었어요 .
    파일 확장자 바꾸는거 통해서 한 번 해보겠습니다

  3. 한상민 2019.01.17 15:22

    https://www.s3carpum.s3.us-east-2.amazonaws.com/ce5e57608a0d5c335d5c0311096c8d0c067b11e0caaf730793a3919967fee20fd33f3383d41a5094930c7e88d45b877a.mp4

    이거를 아예 mp3로 바꾸어서 올려서 해봤는데도 안되요 ㅠㅠ

    "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3" 요거는 되는데
    이러면 서버측 문제일까요?? 아니면 로컬 측 문제일까요??
    혹시 몰라서 코드까지 올려드렸습니다


    var player:AVPlayer?
    var playerItem:AVPlayerItem?
    func setvoice(input : String){
    if input == ""{
    print("record is nil")
    viewcontract?.setrecordnull()
    return
    }
    else{
    print("redcord is not nil")
    var url = URL.init(string: input)
    let playerItem:AVPlayerItem = AVPlayerItem(url: url!)
    player = AVPlayer(playerItem: playerItem)

    viewcontract?.setrecordon()
    return
    }
    }
    func playButtonTapped()
    {

    if player?.rate == 0
    {
    viewcontract?.setrecordstop()
    self.viewcontract?.Toastview(option : 2)
    player!.play()

    } else {

    viewcontract?.setrecordplaying()
    player!.pause()

    }
    }

    • 한상민 2019.01.17 15:23

      input은 url 들어오는 자리입니다

    • 한상민 2019.01.17 15:24

      viewcontract?.~~~~~ 이런형식은 viewcontroller 버튼 이미지 바꿔주는 함수이고여

+ Recent posts