본문 바로가기

공부/iOS

iOS - StackView(3) 애플문서 샘플예제(Dynamic Stack View)

 

안녕하세요. brody입니다.

 

오늘은 저번시간에 이어 StackVIew 3번째 글이에요...

 

이렇게 3번에 나눠서 작성하게 될줄은 몰랐는데 이번 포스팅이 StackView의 마지막 되겠습니다!! 드디어!!!

 

https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/LayoutUsingStackViews.html

애플 공식 문서에 있는 예제를 테스트하면서 StackView를 조금 더 알아볼게요.

 

참고로 위의 공식 문서에서 제공하는 샘플 코드는 실행이 안됩니다. 실행이 되게 Swift 버전 컨버팅  해놓은 소스는 아래에 깃헙에 있어요.

https://github.com/brody424/iOS_StackView_Exam_Cookbook_Swift5

그리고 화면이 엄청 많은데 앞에 3개 화면만 StackView와 관련이 있어요.( DynamicStackView, NestedStackViews, SimpleStackView )

 

 

그럼 공식문서를 번역하면서 ~ StackView 3번째 포스팅 시작할게요!

 

 


 

Dynamic Stack View

 

이 예제에서는 런타임에 스택에서 항목을 동적으로 추가하거나 제거하는 방법을 보여줍니다.

스택에 대한 모든 변경 사항이 애니메이션으로 표시됩니다.

또한 StackView는 ScrollView안에 배치되어 너무 길어서 화면에 맞지 않을 경우 list를 scroll 할 수 있습니다.

 




NOTE
이 예제는 StackView에서 동적으로 작업하고 ScrollView 내에서 StackView로 작업하는 방법을 시연하기 위한 용도로만 사용됩니다.
실제 앱에서는 UITableView를 사용하세요.


Views and Constrains

초기 UI는 매우 간단합니다.

ScrollView를 배치하고 ScrollView의 크기를를 safe area에 꽉차게 설정합니다.

그런 다음 ScrollView 내부에 StackView를 배치하고 StackView 내부에 add item button을 배치합니다.

모든것이 준비되었다면 아래와 같은 제약조건을 설정합니다.

 

Attributes


Attributes inspector에서 다음과 같은 Stack View의 attribute를 설정하하세요.

 

 

CODE


이 예제는 StackView에 항목을 추가하고 StackView에서 제거하려면 약간의 코드가 필요합니다.

viewController를 만들고 ScrollView 와 StackView를 outlet으로 연결하세요.

class DynamicStackViewController: UIViewController {
    
    @IBOutlet weak private var scrollView: UIScrollView!
    @IBOutlet weak private var stackView: UIStackView!
    
    // Method implementations will go here...
    
}


그 다음 viewdidload 메소드에서 scroll view의 초기위치를 설정합니다.

status bar 아래에서 scroll view 내용을 시작합니다.

override func viewDidLoad() {
    super.viewDidLoad()
    
    // setup scrollview
    let insets = UIEdgeInsetsMake(20.0, 0.0, 0.0, 0.0)
    scrollView.contentInset = insets
    scrollView.scrollIndicatorInsets = insets
    
}

이제 add item button 에 action method를 연결합니다.

// MARK: Action Methods
 
@IBAction func addEntry(sender: AnyObject) {
    
    let stack = stackView
    let index = stack.arrangedSubviews.count - 1
    let addView = stack.arrangedSubviews[index]
    
    let scroll = scrollView
    let offset = CGPoint(x: scroll.contentOffset.x,
                         y: scroll.contentOffset.y + addView.frame.size.height)
    
    let newView = createEntry()
    newView.hidden = true
    stack.insertArrangedSubview(newView, atIndex: index)
    
    UIView.animateWithDuration(0.25) { () -> Void in
        newView.hidden = false
        scroll.contentOffset = offset
    }
}



이 메소드는 ScrollView에 대한 새로운 offset을 계산한 다음 새로운 entry view를 만듭니다.

entry view는 숨겨진 상태(hidden)로 stack에 추가됩니다.

숨겨진 view는 Stack의 모양이나 레이아웃에 영향을 주지 않습니다.


entry view를 삭제하는 비슷한 메소드가 아래 있습니다.

그러나 addEntry 메서드와 달리 이 메서드는 Interface Builder와 연결되지 않습니다.

대신, 앱은 view가 만들어질때 각 항목의 view를 이 메서드를 통해서 추가할 수 있습니다.

func deleteStackView(sender: UIButton) {
    if let view = sender.superview {
        UIView.animateWithDuration(0.25, animations: { () -> Void in
            view.hidden = true
        }, completion: { (success) -> Void in
            view.removeFromSuperview()
        })
    }
}



이 메소드는 애니메이션 블록을 사용하여 view를 숨깁니다

애니메이션이 완료되면, view 계층에서 view를 제거합니다.

이렇게 하면 Stack이 arranged views list에서 view가 자동으로 제거됩니다.

entry view는 어떤 뷰든 될수 있습니다. 이 예제에서는 stackView는 date Label, 16진수 문자열을 포함한 date Label, delete button을 포함하고 있습니다.


// MARK: - Private Methods
private func createEntry() -> UIView {
    let date = NSDateFormatter.localizedStringFromDate(NSDate(), dateStyle: .ShortStyle, timeStyle: .NoStyle)
    let number = "\(randomHexQuad())-\(randomHexQuad())-\(randomHexQuad())-\(randomHexQuad())"
    
    let stack = UIStackView()
    stack.axis = .Horizontal
    stack.alignment = .FirstBaseline
    stack.distribution = .Fill
    stack.spacing = 8
    
    let dateLabel = UILabel()
    dateLabel.text = date
    dateLabel.font = UIFont.preferredFontForTextStyle(UIFontTextStyleBody)
    
    let numberLabel = UILabel()
    numberLabel.text = number
    numberLabel.font = UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)
    
    let deleteButton = UIButton(type: .RoundedRect)
    deleteButton.setTitle("Delete", forState: .Normal)
    deleteButton.addTarget(self, action: "deleteStackView:", forControlEvents: .TouchUpInside)
    
    stack.addArrangedSubview(dateLabel)
    stack.addArrangedSubview(numberLabel)
    stack.addArrangedSubview(deleteButton)
    
    return stack
}
 
private func randomHexQuad() -> String {
    return NSString(format: "%X%X%X%X",
                    arc4random() % 16,
                    arc4random() % 16,
                    arc4random() % 16,
                    arc4random() % 16
        ) as String
}
}

 

Discussion


이 예제에서는 StackView의 view가 런타임에 제거되거나 추가되는것을 증명했습니다.

stack의 레이아웃은 arranged view 배열에 대한 변경사항을 자동적으로 조정합니다.

그러나 기억할 만한 몇가지 중요한 사항이 있습니다.

 

 

  • 숨겨진 view는 여전히 stack의 arranged views의 배열안에 있습니다. 하지만 이러한 view는 표시되지 않으며 다른 배열된 view의 레이아웃에 영향을 주지 않습니다.
  • stack의 arranged view 배열에 view를 추가하면 자동으로 view 계층에 추가됩니다.
  • Stack의 arranged views에서 view를 제거해도 view 계층 구조에서 뷰가 자동으로 제거되지는 않지만, view 계층 구조에서 view를 제거하면 arranged view 배열에서 뷰가 제거됩니다.
  • iOS에서 view의 hidden property는 보통 애니메이션을 사용할 수 없습니다. 하지만 이 속성(property)은 Stack의 arranged view array에 배치되는 즉시 해당 view에 대해 애니메이션이 적용 가능한 상태가 됩니다.



이 예제에서는 auto layout를 사용하여 ScrollView를 사용하는 방법도 소개했습니다.

여기서 Stack과 ScrollView 사이의 제약 조건은 scroll view의 내용영역의 크기를 설정합니다.

동일한 width 제약조건은 scrollView를 수평으로 채우도록 Stack(및 내용 크기)를 명시적으로 설정합니다.

수직으로, 내용의 크기는 stack의 fitting size를 기준으로 합니다. stackview는 사용자가 항목을 더 추가할 수록 길어집니다.

콘텐츠가 너무 많아 화면에 맞지 않으면 스크롤이 자동으로 활성화됩니다.

 


드디어.. StackView가 끝났네요.

 

StackView를 쓰면 사용법이 그렇게 어렵지 않아서 간편하게 사용할 수 있고, 런타임에 동적으로 view를 추가하거나 지울 수 있어서 편할거 같아요.

 

그럼 다음 포스팅은 다시 애플문서로 돌아가서 Protecting the User’s Privacy 에 대해서 정리해볼게요.

 

그럼 ㅅㄱㄹ!