ios/SpectrumKitSample/SpectrumKitSample-iOS/SpectrumView.swift (102 lines of code) (raw):
// Copyright (c) Facebook, Inc. and its affiliates.
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
import UIKit
final class SprectrumView: UIView, PickerButtonDelegate, UIGestureRecognizerDelegate {
  // MARK: - Public Properties
  var viewModel: SpectrumViewModel? {
    didSet {
      guard self.viewModel !== oldValue else { return }
      self.viewModel.flatMap(self.updateView(with:))
    }
  }
  // MARK: - Private Properties
  // MARK: IBOutlet
  @IBOutlet private var scrollView: UIScrollView!
  @IBOutlet private var stackView: UIStackView!
  @IBOutlet private var sourceImageView: UIImageView!
  @IBOutlet private var sourceImageButton: UIButton!
  @IBOutlet private var inputTypeButton: UIButton!
  @IBOutlet private var outputTypeButton: UIButton!
  @IBOutlet private var outputFormatButton: UIButton!
  @IBOutlet private var outputFormatContainerView: UIView!
  @IBOutlet private var compressionModeButton: UIButton!
  @IBOutlet private var modeContainerView: UIView!
  @IBOutlet private var qualityContainerView: UIView!
  @IBOutlet private var qualityButton: PickerButton!
  @IBOutlet private var rotationButton: UIButton!
  @IBOutlet private var scalingButton: UIButton!
  @IBOutlet private var croppingButton: UIButton!
  @IBOutlet private var transcodeButton: UIButton!
  // MARK: - NSObject
  deinit {
    NotificationCenter.default.removeObserver(self)
  }
  override func awakeFromNib() {
    super.awakeFromNib()
    self.qualityButton.delegate = self
    self.qualityButton.pickerOptions = SpectrumViewModel.qualityPickerOptions
    self.transcodeButton.layer.cornerRadius = 5
    NotificationCenter.default.addObserver(self, selector: #selector(self.didReceiveKeyboardNotification(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(self.didReceiveKeyboardNotification(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
  }
  // MARK: - PickerButtonDelegate
  func pickerButton(_: UIButton, didPick option: Int) {
    self.viewModel!.quality = option
  }
  // MARK: - UIGestureRecognizerDelegate
  func gestureRecognizer(_: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith _: UIGestureRecognizer) -> Bool {
    return true
  }
  // MARK: - Public Methods
  func updateView(with viewModel: SpectrumViewModel) {
    self.sourceImageView.image = viewModel.source.image
    self.sourceImageButton.setTitle(viewModel.sourceImageDescription, for: .normal)
    self.inputTypeButton.setTitle(viewModel.inputType.title, for: .normal)
    self.outputTypeButton.setTitle(viewModel.outputType.title, for: .normal)
    self.outputFormatButton.setTitle(viewModel.outputFormat.title, for: .normal)
    self.compressionModeButton.setTitle(viewModel.compressionMode.title, for: .normal)
    self.qualityButton.currentOption = viewModel.quality
    self.rotationButton.setTitle(viewModel.rotation.title, for: .normal)
    self.scalingButton.setTitle(viewModel.scaling.title, for: .normal)
    self.croppingButton.setTitle(viewModel.cropping.title, for: .normal)
    self.transcodeButton.isEnabled = viewModel.isTranscodeButtonEnabled
    UIView.performWithoutAnimation {
      self.transcodeButton.setTitle(viewModel.actionButtonTitle, for: .normal)
      self.transcodeButton.layoutIfNeeded()
    }
    self.stackView.isUserInteractionEnabled = viewModel.isTranscoding == false
    if self.modeContainerView.isHidden == viewModel.showsEncodingMode {
      UIView.animate(withDuration: 0.4) {
        self.modeContainerView.isHidden = viewModel.showsEncodingMode == false
      }
    }
    if self.qualityContainerView.isHidden == viewModel.showsQuality {
      UIView.animate(withDuration: 0.4) {
        self.qualityContainerView.isHidden = viewModel.showsQuality == false
      }
    }
    if self.outputFormatContainerView.isHidden == viewModel.showsOutputFormat {
      UIView.animate(withDuration: 0.4) {
        self.outputFormatContainerView.isHidden = viewModel.showsOutputFormat == false
      }
    }
  }
  // MARK: - Private Methods
  @objc private func didReceiveKeyboardNotification(_ notification: Notification) {
    guard let keyboardFrame = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else {
      return
    }
    let intersection = self.convert(self.scrollView.frame, to: nil).intersection(keyboardFrame)
    self.scrollView.contentInset.bottom = intersection.height
  }
  // MARK: IBActions
  @IBAction private func didTapQualityButton(_ sender: UIButton) {
    sender.toggleFirstResponder()
  }
  @IBAction private func didTapBackgroundView(_ gestureRecognizer: UITapGestureRecognizer) {
    // We should only end editing if the tap isn't in the quality button's bounds
    // as its handler already toggles its first responder state. Guarding this handler
    // prevents from making any assumptions in regards to the order in which the gesture
    // and buttons handlers will be called.
    let locationInQualityButton = gestureRecognizer.location(in: self.qualityButton)
    guard self.qualityButton.bounds.contains(locationInQualityButton) == false else { return }
    self.endEditing(true)
  }
  @IBAction private func didTapTranscodeButton(_: UIButton) {
    self.viewModel!.transcode()
  }
}
private extension UIResponder {
  func toggleFirstResponder() {
    if self.isFirstResponder {
      self.resignFirstResponder()
    } else {
      self.becomeFirstResponder()
    }
  }
}