2017-11-06 14 views
0

입력 액세서리보기 높이에 애니메이션을 적용 할 때 이상한 동작이 발생합니다. 내가 도대체 ​​뭘 잘못하고있는 겁니까?입력 부속보기의 높이를 어떻게 애니메이트합니까?

하나의 하위보기가있는 UIInputView 하위 클래스 (InputView)를 만듭니다. InputView 및 해당 intrinsicContentSize의 높이는 하위 뷰에서 제어합니다. InputViewisVisibletrue 일 때 높이가 50 픽셀이고 isVisible이 거짓 일 때 높이가 0 픽셀입니다.

import UIKit 

class InputView: UIInputView { 
    private let someHeight: CGFloat = 50.0, zeroHeight: CGFloat = 0.0 
    private let subView = UIView() 
    private var hide: NSLayoutConstraint?, show: NSLayoutConstraint? 

    var isVisible: Bool { 
     get { 
      return show!.isActive 
     } 
     set { 
      // Always deactivate constraints before activating conflicting ones 
      if newValue == true { 
       hide?.isActive = false 
       show?.isActive = true 
      } else { 
       show?.isActive = false 
       hide?.isActive = true 
      } 
     } 
    } 

    // MARK: Sizing 

    override func sizeThatFits(_ size: CGSize) -> CGSize { 
     return CGSize(width: size.width, height: someHeight) 
    } 

    override var intrinsicContentSize: CGSize { 
     return CGSize.init(width: bounds.size.width, height: subView.bounds.size.height) 
    } 

    // MARK: Initializers 

    required init?(coder aDecoder: NSCoder) { 
     super.init(coder: aDecoder) 
    } 

    override init(frame: CGRect, inputViewStyle: UIInputViewStyle) { 
     super.init(frame: frame, inputViewStyle: inputViewStyle) 

     addSubview(subView) 
     subView.backgroundColor = UIColor.purple 

     translatesAutoresizingMaskIntoConstraints = false 
     subView.translatesAutoresizingMaskIntoConstraints = false 

     subView.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor).isActive = true 
     subView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true 
     subView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true 
     subView.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor).isActive = true 

     show = subView.heightAnchor.constraint(equalToConstant: someHeight) 
     hide = subView.heightAnchor.constraint(equalToConstant: zeroHeight) 
     hide?.isActive = true 
    } 
} 

버튼을 눌렀을 때 호스트 뷰 제어기는 1 초의 애니메이션 블록 isVisible 토글.

import UIKit 

class MainViewController: UIViewController { 
    let testInputView = InputView.init(frame: .zero, inputViewStyle: .default) 

    @IBAction func button(_ sender: AnyObject) { 
     UIView.animate(withDuration: 1.0) { 
      let isVisible = self.testInputView.isVisible 
      self.testInputView.isVisible = !isVisible 
      self.testInputView.layoutIfNeeded() 
     } 
    } 

    override var canBecomeFirstResponder: Bool { 
     return true 
    } 

    override var inputAccessoryView: UIView? { 
     return testInputView 
    } 

    override func viewDidLoad() { 
     super.viewDidLoad() 
    } 
} 

나는 isVisibletrue로 설정되어있을 때 입력 액세서리보기가 원활하게 화면의 botton을에서 성장을 기대하고 isVisiblefalse로 설정되어있을 때 부드럽게 화면의 버튼으로 축소. 대신 키보드 백그라운드 오버레이는 isVisibletrue이고 입력 액세서리보기가 해당 프레임의 센터에서 커지면 전체 50 픽셀 높이로 나타납니다.

The purple box is not growing from the bottom of the screen and is surrounded by the keyboard overlay background

축소, 입력 액세서리보기는 즉시 부드럽게 애니메이션을 계속하기 전에 높이의 일부를 잃는다.

나는이 예기치 않은 동작을 나타내는 input accessory view demonstration project을 만들었습니다.

UIView.animate(withDuration: 1.0) { 
     let isVisible = self.testInputView.isVisible 
     self.testInputView.isVisible = !isVisible 
     self.testInputView.superview?.superview?.layoutIfNeeded() 
    } 

그러나, 애플이 디자인을 변경하는 경우 수퍼를 호출하는 것이 좋습니다는 결코 :

답변

1

이것은 당신에게 올바른 애니메이션을 제공 할 것입니다. 그래서 더 좋은 대답이있을 수 있습니다.

print(testInputView.superview) // UIInputSetHostView 

print(testInputView.superview?.superview) // UIInputSetContainerView 

편집 : 추가 된 안전한 솔루션

나는 UIInputView 너무 익숙한 아니에요을

이것은 superviews이 나타내는 것입니다.

1 단계 : 애니메이션 블록 외부
이동합니다에서 IsVisible에만 서브 뷰의 높이 변화를 애니메이션에 그러나 수퍼를 호출하지 않고 그것을 해결하는 하나 개의 방법이 될 것이다.

@IBAction func button(_ sender: AnyObject) { 
    let isVisible = self.testInputView.isVisible 
    self.testInputView.isVisible = !isVisible 
    UIView.animate(withDuration: 1.0) { 
     self.testInputView.layoutIfNeeded() 
    } 
} 

2 단계 : 대신 intrinsicContentSize의 InputView의 높이 제약 조건을 변경하여 InputView의 새로운 방법을 만듭니다.

private func updateHeightConstraint(height: CGFloat) { 
    for constraint in constraints { 
     if constraint.firstAttribute == .height { 
      constraint.constant = height 
     } 
    } 
    self.layoutIfNeeded() 
} 

3 단계 : 그리고 세터 내부 메소드를 호출한다.

if newValue == true { 
    updateHeightConstraint(height: someHeight) 
    hide?.isActive = false 
    show?.isActive = true 
} else { 
    updateHeightConstraint(height: zeroHeight) 
    show?.isActive = false 
    hide?.isActive = true 
} 

4 단계 : 초기화에 마지막으로 일부 변경.

override init(frame: CGRect, inputViewStyle: UIInputViewStyle) { 
    super.init(frame: frame, inputViewStyle: inputViewStyle) 

    addSubview(subView) 

    backgroundColor = .clear 
    subView.backgroundColor = UIColor.purple 

    subView.translatesAutoresizingMaskIntoConstraints = false 

    subView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true 
    subView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true 
    subView.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor).isActive = true 

    show = subView.heightAnchor.constraint(equalToConstant: someHeight) 
    hide = subView.heightAnchor.constraint(equalToConstant: zeroHeight) 
    hide?.isActive = true 
} 

결론 : InputView 이러한 결과는 보라색 서브 뷰의 높이를 애니메이션하기 전에 높이의 변경합니다. 유일한 단점은 UIInputView입니다. UIInputView는 기본적으로 회색 배경을 가지며 지우기로 변경할 수 없습니다. 그러나 VC와 동일한 backgroundColor를 사용할 수 있습니다.

대신 InputAccessoryView로 일반 UIView를 사용해야하는 경우 기본적으로 UIColor.clear가됩니다. 첫 번째 점프보다 눈에 띄지 않습니다.

+0

더 안전한 해결책을 밝히기를 바라는 최고의 답 *으로 받아들이지 않겠지 만,이 방법은 아름답게 작동합니다. 슈퍼 뷰를 찾는 방법에 대한 팁 또한 매우 유용합니다. –