iOS_System View & Control(2)

longlivedrgn·2022년 9월 29일
0

iOS 스터디

목록 보기
2/8
post-thumbnail

StackView

  • 가로나 세로로 view들을 차곡차곡 채울 수 있는 view

Horizontal & Vertical

class StackViewAxisViewController: UIViewController {
    
    @IBOutlet weak var stackView: UIStackView!
    
    
    // view가 horizontal에서 vertical로 바뀌는 혹은 그 반대로 바뀌는 animation을 호출하는 action 생성
    @IBAction func toggleAxis(_ sender: Any) {
        UIView.animate(withDuration: 0.3){[self] in
            if stackView.axis == .horizontal {
                stackView.axis = .vertical
            } else {
                stackView.axis = .horizontal
            }
        }
    }
  • 아래와 같이 있던 stackview가 toggleAxis를 누르면

  • 아래와 같이 stackview가 horizontal하게 변하게 된다.

Spacing

  • 먼저 아래와 같이 stackview를 구성해보자(현재 spacing은 음수)

  • 아래는 slider를 통하여 넘어간 value값이 stackview의 spacing으로 넘어가게 된다.

class StackViewSpacingViewController: UIViewController {
    
    @IBOutlet weak var redView: UIView!
    @IBOutlet weak var horizontalStackView: UIStackView!
    @IBOutlet weak var spacingLabel: UILabel!
    @IBOutlet weak var spacingSlider: UISlider!
    
    @IBAction func spacingChanged(_ sender: UISlider) {
        UIView.animate(withDuration: 0.3){
            self.horizontalStackView.spacing = CGFloat(sender.value)
        }
        updateLabel()
    }
    
    private func updateLabel() {
        spacingLabel.text = "\(Int(horizontalStackView.spacing))"
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        updateLabel()
        spacingSlider.value = Float(horizontalStackView.spacing)
    }
}
class StackViewSpacingViewController: UIViewController {
    
    @IBOutlet weak var redView: UIView!
    @IBOutlet weak var horizontalStackView: UIStackView!
    @IBOutlet weak var spacingLabel: UILabel!
    @IBOutlet weak var spacingSlider: UISlider!
    
    // slider 값을 stackview의 SPACING 값으로 넣기
    @IBAction func spacingChanged(_ sender: UISlider) {
        UIView.animate(withDuration: 0.3){
            self.horizontalStackView.spacing = CGFloat(sender.value)
        }
        updateLabel()
    }
    
    private func updateLabel() {
        spacingLabel.text = "\(Int(horizontalStackView.spacing))"
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        updateLabel()
        spacingSlider.value = Float(horizontalStackView.spacing)
//
//        //
//        horizontalStackView.setCustomSpacing(60, after: redView)
    }
}

※ superview에 바로 view를 추가하면 subview로 들어가지만, stackview에 view를 추가하면 arrangedsubview로 들어가게 된다.

StackView에 view 추가,제거

cf) https://ios-development.tistory.com/986 - layoutIfNeeded()

import UIKit

class StackViewArrangedSubviewsViewController: UIViewController {
    
    @IBOutlet weak var stackView: UIStackView!
    
    // 뒤에 view를 넣어주기
    @IBAction func add(_ sender: Any) {
        let v = generateView()
        stackView.addArrangedSubview(v)
        
        UIView.animate(withDuration: 0.3){
            self.stackView.layoutIfNeeded()
        }
    }
    
    // index 0에 view를 추가해주기
    @IBAction func insert(_ sender: Any) {
        let v = generateView()
        stackView.insertArrangedSubview(v, at: 0)
        
        UIView.animate(withDuration: 0.3){
            self.stackView.layoutIfNeeded()
        }
    }
    
    // 랜덤으로 view를 삭제하기
    @IBAction func remove(_ sender: Any) {
    // 먼저 stackview에 subview가 있는 지 확인하기
        guard stackView.arrangedSubviews.count > 0 else {
            return
        }
        
        let index = Int.random(in: 0..<stackView.arrangedSubviews.count)
        let v = stackView.arrangedSubviews[index]

        // remove가 되는 것은 animation을 조금 다르게 설정해줘야된다. - 0.3초동안 v를 hidden하고, 그 이후에는 remove한다!
        UIView.animate(withDuration: 0.3){
            v.isHidden = true
        } completion: {finished in
            self.stackView.removeArrangedSubview(v)
        }
    }
    
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // viewDidload에서 바로 view를 넣고싶다면?
        // stackview에는 바로 addsubview가 되지 않는다. 따라서 frame을 설정하고 해줘야된다.
        let v = generateView()
        v.frame = stackView.bounds
        stackView.addSubview(v)
        
    }
}

extension StackViewArrangedSubviewsViewController {
    private func generateView() -> UIView {
        let v = UIView()
        let r = CGFloat.random(in: 0.0 ... 256.0) / 255
        let g = CGFloat.random(in: 0.0 ... 256.0) / 255
        let b = CGFloat.random(in: 0.0 ... 256.0) / 255
        v.backgroundColor = UIColor(red: r, green: g, blue: b, alpha: 1.0)
        
        return v
    }
}

Alert

Alert 스타일

세개의 .alert 스타일이 있다. -> .default / .cancel / destructive
preferredAction을 사용하면 우선순위도 올라가고 볼드체로 변하게 된다. 단, addAction을 한 후에만 사용이 가능하다.

  • 아래와 같이 show action을 추가해주자.
class AlertViewController: UIViewController {
    
    @IBAction func show(_ sender: Any) {
        // alert vs actionsheet
        let controller = UIAlertController(title: "Hello", message: "Have a nice day :)", preferredStyle: .alert)
        
        // Ok button을 만들어주기. 뒤에는 클로져인데, action을 실행(버튼 클릭)하면 클로져가 실행이된다.
        let okAction = UIAlertAction(title: "Ok", style: .default) { (action) in
            print(action.title)
        }
        
        // Ok 버튼 추가해주기
        controller.addAction(okAction)
        
        // canel 버튼을 만들어주자!.
        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (action) in
            print(action.title)
        }
        
        // cancel 버튼 추가해주기
        controller.addAction(cancelAction)
        
        // Destructive style의 버튼 만들어보기
        let destructive = UIAlertAction(title: "Destructive", style: .destructive){ (action) in
            print(action.title)
        }
        // destructive 버튼 넣어주기
        controller.addAction(destructive)
        
        // alert는 cancel 타입의 버튼을 볼드체로 보여준다. -> 그러나 preferred를 하면 우선순위도 높아지고, 볼드체도 된다.
        controller.preferredAction = okAction
            
        // alertcontroller를 보여준다!
        present(controller, animated: true, completion: nil)
    }
  • 위 코드의 결과는 아래와 같다.

경고창을 통하여 화면 로그인하기

현재 아래와 같이 viewcontroller가 구성이되어있다

import UIKit

class AddTextFieldViewController: UIViewController {
    
    @IBOutlet weak var idLabel: UILabel!
    @IBOutlet weak var passwordLabel: UILabel!
    
    @IBAction func show(_ sender: Any) {
        
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        
    }
}
  • 아래와 같이 스토리보드가 구성이되어있다.

  • 아래와 같이 코드를 구성하여 경고창에 text를 입력하면 실제 view에 해당 text가 입력이 되도록 변경을 할 수 있다.

@IBAction func show(_ sender: Any) {
        // 경고창을 띄우기!
        let controller = UIAlertController(title: "Sign In to iTunes Store", message: nil, preferredStyle: .alert)
        
        // 경고창에 textfield 생성하기
        controller.addTextField{(idField) in
            // placeholder 생성하기
            idField.placeholder = "Apple Id"
        }
        
        controller.addTextField{(passwordField) in
            passwordField.placeholder = "Input Password"
            // 마스킹 하기
            passwordField.isSecureTextEntry = true
        }
        
        let okAction = UIAlertAction(title: "Ok", style: .default){[weak self]
            (action) in
            if let fieldList = controller.textFields{
                // 경고창 첫번째 textfield를 idLabel에 넣기
                if let idField = fieldList.first{
                    self?.idLabel.text = idField.text
                }
                
                if let passwordField = fieldList.last {
                    self?.passwordLabel.text = passwordField.text
                }
            }
        }
        controller.addAction(okAction)
        
        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
        controller.addAction(cancelAction)
        
        present(controller, animated: true, completion: nil)
    }
  • 실제 로그인창과 같이 placeholder와 비밀번호는 마스킹하는 작업을 해보자. 앞서 비워둔 addTextField 코드에서 코드를 추가해준다.
// 경고창에 textfield 생성하기
        controller.addTextField{(idField) in
            // placeholder 생성하기
            idField.placeholder = "Apple Id"
        }
        
        controller.addTextField{(passwordField) in
            passwordField.placeholder = "Input Password"
            // 마스킹 하기
            passwordField.isSecureTextEntry = true
        }
  • 아래와 같이 경고창에 text를 입력하면
  • 아래와 같이 label이 변하게 된다.

ActionSheet

  • 먼저 alert로 설정된 코드를 보자
class ActionSheetViewController: UIViewController {
    
    @IBOutlet weak var resultLabel: UILabel!
    
    @IBAction func show(_ sender: UIButton) {
        let controller = UIAlertController(title: "Languages", message: "Choose one", preferredStyle: .alert)
        
        let swiftAction = UIAlertAction(title: "Swift", style: .default) { [weak self] (action) in
            self?.resultLabel.text = action.title
        }
        controller.addAction(swiftAction)
        
        let javaAction = UIAlertAction(title: "Java", style: .default) { [weak self] (action) in
            self?.resultLabel.text = action.title
        }
        controller.addAction(javaAction)
        
        let pythonAction = UIAlertAction(title: "Python", style: .default) { [weak self] (action) in
            self?.resultLabel.text = action.title
        }
        controller.addAction(pythonAction)
        
        let cSharpAction = UIAlertAction(title: "C#", style: .default) { [weak self] (action) in
            self?.resultLabel.text = action.title
        }
        controller.addAction(cSharpAction)
        
        let clearAction = UIAlertAction(title: "Clear", style: .destructive) { [weak self] (action) in
            self?.resultLabel.text = nil
        }
        controller.addAction(clearAction)
        
        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
        controller.addAction(cancelAction)
        
        present(controller, animated: true, completion: nil)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        
    }
}
  • 위와 같이 alert style로 설정이된 경우 아래와 같이 경과창이 뜨게된다.

  • 그러나 만약 아래와 style를 actionsheet로 설정을 할 경우 아래와 같이 변하게 된다.

  • 그러나 actionsheet의 경우 아이패드에서 사이즈를 설정을 해줘야지 크래쉬가 일어나지 않는다. 아래와 같은 코드를 추가해준다.
        if let pc = controller.popoverPresentationController{
            pc.sourceView = view
            pc.sourceRect = sender.frame
        }

만약 이미 설정한 action이 있다면 그 액션은 다시 설정 못하게하기

  • 아래와 같은 코드 추가해주기
        // 만약 이미 설정한 action이 있다면 그 액션은 다시 설정을 못하게하기
        for action in controller.actions {
            if action.title == resultLabel.text {
                action.isEnabled = false
            }
        }
  • 아래와 같이 이미 설정된 것은 다시 설정을 못한다.

0개의 댓글