Minimize use of implicitly unwrapped optionals in code.

And mark `init(coder:)` as unavailable.
This commit is contained in:
Igor Solomennikov 2022-03-02 13:31:10 -08:00
parent 09048dad07
commit 45db3a963e
3 changed files with 144 additions and 176 deletions

View File

@ -562,7 +562,7 @@ private class TitleView: UIView {
private let label = UILabel() private let label = UILabel()
private let iconView = UIImageView() private let iconView = UIImageView()
private var stackView: UIStackView! private let stackView: UIStackView
// Returns same font as UIBarButtonItem uses. // Returns same font as UIBarButtonItem uses.
private func titleLabelFont() -> UIFont { private func titleLabelFont() -> UIFont {
@ -573,18 +573,10 @@ private class TitleView: UIView {
// MARK: - Initializers // MARK: - Initializers
override init(frame: CGRect) { override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
stackView = UIStackView(arrangedSubviews: [label, iconView]) stackView = UIStackView(arrangedSubviews: [label, iconView])
super.init(frame: frame)
stackView.axis = .horizontal stackView.axis = .horizontal
stackView.alignment = .center stackView.alignment = .center
stackView.spacing = 5 stackView.spacing = 5
@ -605,6 +597,11 @@ private class TitleView: UIView {
addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(titleTapped))) addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(titleTapped)))
} }
@available(*, unavailable, message: "Use init(frame:) instead")
required init?(coder: NSCoder) {
notImplemented()
}
override func tintColorDidChange() { override func tintColorDidChange() {
super.tintColorDidChange() super.tintColorDidChange()
label.textColor = tintColor label.textColor = tintColor

View File

@ -40,8 +40,8 @@ class CameraCaptureControl: UIView {
private static let shutterButtonDefaultSize: CGFloat = 72 private static let shutterButtonDefaultSize: CGFloat = 72
private static let shutterButtonRecordingSize: CGFloat = 122 private static let shutterButtonRecordingSize: CGFloat = 122
private var outerCircleSizeConstraint: NSLayoutConstraint! private let outerCircleSizeConstraint: NSLayoutConstraint
private var innerCircleSizeConstraint: NSLayoutConstraint! private let innerCircleSizeConstraint: NSLayoutConstraint
private var slidingCircleHPositionConstraint: NSLayoutConstraint! private var slidingCircleHPositionConstraint: NSLayoutConstraint!
private var slidingCircleVPositionConstraint: NSLayoutConstraint! private var slidingCircleVPositionConstraint: NSLayoutConstraint!
@ -67,23 +67,13 @@ class CameraCaptureControl: UIView {
weak var delegate: CameraCaptureControlDelegate? weak var delegate: CameraCaptureControlDelegate?
convenience init(axis: NSLayoutConstraint.Axis) { required init(axis: NSLayoutConstraint.Axis) {
self.init(frame: CGRect(origin: .zero, size: CameraCaptureControl.intrinsicContentSize(forAxis: axis))) innerCircleSizeConstraint = shutterButtonInnerCircle.autoSetDimension(.width, toSize: CameraCaptureControl.shutterButtonDefaultSize)
outerCircleSizeConstraint = shutterButtonOuterCircle.autoSetDimension(.width, toSize: CameraCaptureControl.shutterButtonDefaultSize)
super.init(frame: CGRect(origin: .zero, size: CameraCaptureControl.intrinsicContentSize(forAxis: axis)))
self.axis = axis self.axis = axis
reactivateConstraintsForCurrentAxis()
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
// Round Shutter Button // Round Shutter Button
addLayoutGuide(shutterButtonLayoutGuide) addLayoutGuide(shutterButtonLayoutGuide)
@ -104,11 +94,9 @@ class CameraCaptureControl: UIView {
addSubview(shutterButtonOuterCircle) addSubview(shutterButtonOuterCircle)
shutterButtonOuterCircle.centerXAnchor.constraint(equalTo: shutterButtonLayoutGuide.centerXAnchor).isActive = true shutterButtonOuterCircle.centerXAnchor.constraint(equalTo: shutterButtonLayoutGuide.centerXAnchor).isActive = true
shutterButtonOuterCircle.centerYAnchor.constraint(equalTo: shutterButtonLayoutGuide.centerYAnchor).isActive = true shutterButtonOuterCircle.centerYAnchor.constraint(equalTo: shutterButtonLayoutGuide.centerYAnchor).isActive = true
outerCircleSizeConstraint = shutterButtonOuterCircle.autoSetDimension(.width, toSize: CameraCaptureControl.shutterButtonDefaultSize)
shutterButtonOuterCircle.autoPin(toAspectRatio: 1) shutterButtonOuterCircle.autoPin(toAspectRatio: 1)
addSubview(shutterButtonInnerCircle) addSubview(shutterButtonInnerCircle)
innerCircleSizeConstraint = shutterButtonInnerCircle.autoSetDimension(.width, toSize: CameraCaptureControl.shutterButtonDefaultSize)
shutterButtonInnerCircle.autoPin(toAspectRatio: 1) shutterButtonInnerCircle.autoPin(toAspectRatio: 1)
shutterButtonInnerCircle.isUserInteractionEnabled = false shutterButtonInnerCircle.isUserInteractionEnabled = false
shutterButtonInnerCircle.backgroundColor = .clear shutterButtonInnerCircle.backgroundColor = .clear
@ -119,13 +107,18 @@ class CameraCaptureControl: UIView {
// The long press handles both the tap and the hold interaction, as well as the animation // The long press handles both the tap and the hold interaction, as well as the animation
// the presents as the user begins to hold (and the button begins to grow prior to recording) // the presents as the user begins to hold (and the button begins to grow prior to recording)
longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress)) let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress))
longPressGesture.minimumPressDuration = 0 longPressGesture.minimumPressDuration = 0
shutterButtonOuterCircle.addGestureRecognizer(longPressGesture) shutterButtonOuterCircle.addGestureRecognizer(longPressGesture)
reactivateConstraintsForCurrentAxis() reactivateConstraintsForCurrentAxis()
} }
@available(*, unavailable, message: "Use init(axis:) instead")
required init?(coder: NSCoder) {
notImplemented()
}
// MARK: - UI State // MARK: - UI State
enum State { enum State {
@ -154,12 +147,13 @@ class CameraCaptureControl: UIView {
} }
} }
func setState(_ state: State, animationDuration: TimeInterval = 0) { func setState(_ state: State, isRecordingWithLongPress: Bool = false, animationDuration: TimeInterval = 0) {
guard _internalState != state else { return } guard _internalState != state else { return }
Logger.debug("New state: \(_internalState) -> \(state)") Logger.debug("New state: \(_internalState) -> \(state)")
_internalState = state _internalState = state
self.isRecordingWithLongPress = isRecordingWithLongPress
if animationDuration > 0 { if animationDuration > 0 {
UIView.animate(withDuration: animationDuration, UIView.animate(withDuration: animationDuration,
delay: 0, delay: 0,
@ -190,12 +184,11 @@ class CameraCaptureControl: UIView {
case .recording: case .recording:
prepareRecordingControlsIfNecessary() prepareRecordingControlsIfNecessary()
let recordingWithLongPress = longPressGesture.state != .possible let sliderProgress = isRecordingWithLongPress ? sliderTrackingProgress : 0
let sliderProgress = recordingWithLongPress ? sliderTrackingProgress : 0
// element visibility // element visibility
stopButton.isHidden = sliderProgress == 0 stopButton.isHidden = sliderProgress == 0
slidingCircleView.isHidden = sliderProgress == 0 slidingCircleView.isHidden = sliderProgress == 0
lockIconView.isHidden = !recordingWithLongPress lockIconView.isHidden = !isRecordingWithLongPress
lockIconView.setState(sliderProgress > 0.5 ? .locking : .unlocked, animated: true) lockIconView.setState(sliderProgress > 0.5 ? .locking : .unlocked, animated: true)
shutterButtonInnerCircle.backgroundColor = .ows_white shutterButtonInnerCircle.backgroundColor = .ows_white
// element sizes // element sizes
@ -307,7 +300,7 @@ class CameraCaptureControl: UIView {
// MARK: - Gestures // MARK: - Gestures
private var longPressGesture: UILongPressGestureRecognizer! private var isRecordingWithLongPress = false
private static let longPressDurationThreshold = 0.5 private static let longPressDurationThreshold = 0.5
private static let minDistanceBeforeActivatingLockSlider: CGFloat = 30 private static let minDistanceBeforeActivatingLockSlider: CGFloat = 30
private var initialTouchLocation: CGPoint? private var initialTouchLocation: CGPoint?
@ -340,7 +333,7 @@ class CameraCaptureControl: UIView {
) { [weak self] _ in ) { [weak self] _ in
guard let self = self else { return } guard let self = self else { return }
self.setState(.recording, animationDuration: 0.4) self.setState(.recording, isRecordingWithLongPress: true, animationDuration: 0.4)
self.delegate?.cameraCaptureControlDidRequestStartVideoRecording(self) self.delegate?.cameraCaptureControlDidRequestStartVideoRecording(self)
} }
@ -471,10 +464,10 @@ class CameraCaptureControl: UIView {
private class LockView: UIView { private class LockView: UIView {
private var imageViewLock = UIImageView(image: UIImage(named: "media-composer-lock-outline-24")) private let imageViewLock = UIImageView(image: UIImage(named: "media-composer-lock-outline-24"))
private var blurBackgroundView = CircleBlurView(effect: UIBlurEffect(style: .dark)) private let blurBackgroundView = CircleBlurView(effect: UIBlurEffect(style: .dark))
private var whiteBackgroundView = CircleView() private let whiteBackgroundView = CircleView()
private var whiteCircleView = CircleView() private let whiteCircleView = CircleView()
enum State { enum State {
case unlocked case unlocked
@ -531,15 +524,7 @@ private class LockView: UIView {
override init(frame: CGRect) { override init(frame: CGRect) {
super.init(frame: frame) super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
isUserInteractionEnabled = false isUserInteractionEnabled = false
addSubview(blurBackgroundView) addSubview(blurBackgroundView)
@ -562,6 +547,11 @@ private class LockView: UIView {
updateAppearance() updateAppearance()
} }
@available(*, unavailable, message: "Use init(frame:) instead")
required init?(coder: NSCoder) {
notImplemented()
}
override var intrinsicContentSize: CGSize { override var intrinsicContentSize: CGSize {
CGSize(width: CameraCaptureControl.recordingLockControlSize, height: CameraCaptureControl.recordingLockControlSize) CGSize(width: CameraCaptureControl.recordingLockControlSize, height: CameraCaptureControl.recordingLockControlSize)
} }
@ -621,7 +611,11 @@ class CameraOverlayButton: UIButton, UserInterfaceStyleOverride {
} }
} }
private var backgroundView: UIVisualEffectView! private let backgroundView: CircleBlurView = {
let view = CircleBlurView(effect: UIBlurEffect(style: .regular))
view.isUserInteractionEnabled = false
return view
}()
private static let visibleButtonSize: CGFloat = 36 // both height and width private static let visibleButtonSize: CGFloat = 36 // both height and width
private static let defaultInset: CGFloat = 4 private static let defaultInset: CGFloat = 4
@ -632,32 +626,28 @@ class CameraOverlayButton: UIButton, UserInterfaceStyleOverride {
} }
} }
convenience init(image: UIImage?, userInterfaceStyleOverride: UIUserInterfaceStyle = .unspecified) { required init(image: UIImage?, userInterfaceStyleOverride: UIUserInterfaceStyle = .unspecified) {
self.init(frame: CGRect(origin: .zero, size: .square(Self.visibleButtonSize + 2*Self.defaultInset))) super.init(frame: CGRect(origin: .zero, size: .square(Self.visibleButtonSize + 2*Self.defaultInset)))
self.userInterfaceStyleOverride = userInterfaceStyleOverride self.userInterfaceStyleOverride = userInterfaceStyleOverride
layoutMargins = contentInsets
addSubview(backgroundView)
backgroundView.autoPinEdgesToSuperviewMargins()
setImage(image, for: .normal) setImage(image, for: .normal)
updateStyle() updateStyle()
} }
private override init(frame: CGRect) { @available(*, unavailable, message: "Use init(image:userInterfaceStyleOverride:) instead")
super.init(frame: frame) override init(frame: CGRect) {
commonInit() notImplemented()
} }
@available(*, unavailable, message: "Use init(image:userInterfaceStyleOverride:) instead")
required init?(coder: NSCoder) { required init?(coder: NSCoder) {
super.init(coder: coder) notImplemented()
commonInit()
}
private func commonInit() {
layoutMargins = contentInsets
backgroundView = CircleBlurView(effect: UIBlurEffect(style: CameraOverlayButton.blurEffectStyle(for: effectiveUserInterfaceStyle)))
backgroundView.isUserInteractionEnabled = false
addSubview(backgroundView)
backgroundView.autoPinEdgesToSuperviewMargins()
updateStyle()
} }
override func layoutSubviews() { override func layoutSubviews() {
@ -700,14 +690,8 @@ class MediaDoneButton: UIButton, UserInterfaceStyleOverride {
} }
} }
override init(frame: CGRect) { private static var font: UIFont {
super.init(frame: frame) return UIFont.ows_dynamicTypeSubheadline.ows_monospaced
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
} }
private let numberFormatter: NumberFormatter = { private let numberFormatter: NumberFormatter = {
@ -720,22 +704,38 @@ class MediaDoneButton: UIButton, UserInterfaceStyleOverride {
let label = UILabel() let label = UILabel()
label.textColor = .ows_white label.textColor = .ows_white
label.textAlignment = .center label.textAlignment = .center
label.font = .ows_dynamicTypeSubheadline.ows_monospaced label.font = MediaDoneButton.font
return label return label
}() }()
private var pillView: PillView! private let pillView: PillView = {
private var blurBackgroundView: UIVisualEffectView! let pillView = PillView(frame: .zero)
private var chevronImageView: UIImageView!
private var dimmerView: UIView!
private func commonInit() {
pillView = PillView(frame: bounds)
pillView.isUserInteractionEnabled = false pillView.isUserInteractionEnabled = false
pillView.layoutMargins = UIEdgeInsets(hMargin: 8, vMargin: 7) pillView.layoutMargins = UIEdgeInsets(hMargin: 8, vMargin: 7)
return pillView
}()
private let blurBackgroundView = UIVisualEffectView(effect: UIBlurEffect(style: .regular))
private let chevronImageView: UIImageView = {
let image: UIImage?
if #available(iOS 13, *) {
image = CurrentAppContext().isRTL ? UIImage(systemName: "chevron.backward") : UIImage(systemName: "chevron.right")
} else {
image = CurrentAppContext().isRTL ? UIImage(named: "chevron-left-20") : UIImage(named: "chevron-right-20")
}
let chevronImageView = UIImageView(image: image!.withRenderingMode(.alwaysTemplate))
chevronImageView.contentMode = .center
if #available(iOS 13, *) {
chevronImageView.preferredSymbolConfiguration = UIImage.SymbolConfiguration(pointSize: MediaDoneButton.font.pointSize)
}
return chevronImageView
}()
private var dimmerView: UIView?
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(pillView) addSubview(pillView)
pillView.autoPinEdgesToSuperviewEdges() pillView.autoPinEdgesToSuperviewEdges()
blurBackgroundView = UIVisualEffectView(effect: UIBlurEffect(style: MediaDoneButton.blurEffectStyle(for: effectiveUserInterfaceStyle)))
pillView.addSubview(blurBackgroundView) pillView.addSubview(blurBackgroundView)
blurBackgroundView.autoPinEdgesToSuperviewEdges() blurBackgroundView.autoPinEdgesToSuperviewEdges()
@ -745,18 +745,6 @@ class MediaDoneButton: UIButton, UserInterfaceStyleOverride {
blueBadgeView.addSubview(textLabel) blueBadgeView.addSubview(textLabel)
textLabel.autoPinEdgesToSuperviewMargins() textLabel.autoPinEdgesToSuperviewMargins()
let image: UIImage?
if #available(iOS 13, *) {
image = CurrentAppContext().isRTL ? UIImage(systemName: "chevron.backward") : UIImage(systemName: "chevron.right")
} else {
image = CurrentAppContext().isRTL ? UIImage(named: "chevron-left-20") : UIImage(named: "chevron-right-20")
}
chevronImageView = UIImageView(image: image!.withRenderingMode(.alwaysTemplate))
chevronImageView.contentMode = .center
if #available(iOS 13, *) {
chevronImageView.preferredSymbolConfiguration = UIImage.SymbolConfiguration(pointSize: textLabel.font.pointSize)
}
let hStack = UIStackView(arrangedSubviews: [blueBadgeView, chevronImageView]) let hStack = UIStackView(arrangedSubviews: [blueBadgeView, chevronImageView])
hStack.spacing = 6 hStack.spacing = 6
pillView.addSubview(hStack) pillView.addSubview(hStack)
@ -765,6 +753,11 @@ class MediaDoneButton: UIButton, UserInterfaceStyleOverride {
updateStyle() updateStyle()
} }
@available(*, unavailable, message: "Use init(frame:) instead")
required init?(coder: NSCoder) {
notImplemented()
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
if traitCollection.preferredContentSizeCategory != previousTraitCollection?.preferredContentSizeCategory { if traitCollection.preferredContentSizeCategory != previousTraitCollection?.preferredContentSizeCategory {
textLabel.font = .ows_dynamicTypeSubheadline.ows_monospaced textLabel.font = .ows_dynamicTypeSubheadline.ows_monospaced
@ -781,13 +774,14 @@ class MediaDoneButton: UIButton, UserInterfaceStyleOverride {
didSet { didSet {
if isHighlighted { if isHighlighted {
if dimmerView == nil { if dimmerView == nil {
dimmerView = UIView(frame: bounds) let dimmerView = UIView(frame: bounds)
dimmerView.isUserInteractionEnabled = false dimmerView.isUserInteractionEnabled = false
dimmerView.backgroundColor = .ows_black dimmerView.backgroundColor = .ows_black
pillView.addSubview(dimmerView) pillView.addSubview(dimmerView)
dimmerView.autoPinEdgesToSuperviewEdges() dimmerView.autoPinEdgesToSuperviewEdges()
self.dimmerView = dimmerView
} }
dimmerView.alpha = 0.5 dimmerView?.alpha = 0.5
} else if let dimmerView = dimmerView { } else if let dimmerView = dimmerView {
dimmerView.alpha = 0 dimmerView.alpha = 0
} }

View File

@ -707,45 +707,38 @@ private struct ButtonImages {
} }
private class TopBar: UIView { private class TopBar: UIView {
private(set) var closeButton: CameraOverlayButton! let closeButton = CameraOverlayButton(image: ButtonImages.close, userInterfaceStyleOverride: .dark)
private var cameraControlsContainerView: UIView! private let cameraControlsContainerView: UIStackView
private(set) var flashModeButton: CameraOverlayButton! let flashModeButton = CameraOverlayButton(image: ButtonImages.flashAuto, userInterfaceStyleOverride: .dark)
private(set) var batchModeButton: CameraOverlayButton! let batchModeButton = CameraOverlayButton(image: ButtonImages.flashAuto, userInterfaceStyleOverride: .dark)
private(set) var recordingTimerView: RecordingTimerView! let recordingTimerView = RecordingTimerView(frame: .zero)
override init(frame: CGRect) { override init(frame: CGRect) {
cameraControlsContainerView = UIStackView(arrangedSubviews: [ batchModeButton, flashModeButton ])
super.init(frame: frame) super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
layoutMargins = UIEdgeInsets(hMargin: 8, vMargin: 4) layoutMargins = UIEdgeInsets(hMargin: 8, vMargin: 4)
closeButton = CameraOverlayButton(image: ButtonImages.close, userInterfaceStyleOverride: .dark)
addSubview(closeButton) addSubview(closeButton)
closeButton.autoPinHeightToSuperviewMargins() closeButton.autoPinHeightToSuperviewMargins()
closeButton.autoPinLeadingToSuperviewMargin() closeButton.autoPinLeadingToSuperviewMargin()
recordingTimerView = RecordingTimerView(frame: .zero)
addSubview(recordingTimerView) addSubview(recordingTimerView)
recordingTimerView.autoPinHeightToSuperview(withMargin: 8) recordingTimerView.autoPinHeightToSuperview(withMargin: 8)
recordingTimerView.autoHCenterInSuperview() recordingTimerView.autoHCenterInSuperview()
flashModeButton = CameraOverlayButton(image: ButtonImages.flashAuto, userInterfaceStyleOverride: .dark) cameraControlsContainerView.spacing = 16
batchModeButton = CameraOverlayButton(image: ButtonImages.batchModeOff, userInterfaceStyleOverride: .dark) addSubview(cameraControlsContainerView)
let stackView = UIStackView(arrangedSubviews: [ batchModeButton, flashModeButton ]) cameraControlsContainerView.autoPinHeightToSuperviewMargins()
stackView.spacing = 16 cameraControlsContainerView.autoPinTrailingToSuperviewMargin()
addSubview(stackView) }
stackView.autoPinHeightToSuperviewMargins()
stackView.autoPinTrailingToSuperviewMargin() @available(*, unavailable, message: "Use init(frame:) instead")
cameraControlsContainerView = stackView required init?(coder: NSCoder) {
notImplemented()
} }
// MARK: - Mode // MARK: - Mode
@ -760,12 +753,12 @@ private class TopBar: UIView {
case .cameraControls: case .cameraControls:
closeButton.isHidden = false closeButton.isHidden = false
cameraControlsContainerView.isHidden = false cameraControlsContainerView.isHidden = false
recordingTimerView?.isHidden = true recordingTimerView.isHidden = true
case .closeButton: case .closeButton:
closeButton.isHidden = false closeButton.isHidden = false
cameraControlsContainerView.isHidden = true cameraControlsContainerView.isHidden = true
recordingTimerView?.isHidden = true recordingTimerView.isHidden = true
case .videoRecording: case .videoRecording:
closeButton.isHidden = true closeButton.isHidden = true
@ -793,47 +786,36 @@ private class BottomBar: UIView {
} }
} }
private(set) var photoLibraryButton: MediaPickerThumbnailButton! let photoLibraryButton = MediaPickerThumbnailButton(frame: .zero)
private(set) var switchCameraButton: CameraOverlayButton! let switchCameraButton = CameraOverlayButton(image: ButtonImages.switchCamera, userInterfaceStyleOverride: .dark)
let controlButtonsLayoutGuide = UILayoutGuide() // area encompassing Photo Library and Switch Camera buttons. let controlButtonsLayoutGuide = UILayoutGuide() // area encompassing Photo Library and Switch Camera buttons.
private(set) var captureControl: CameraCaptureControl! let captureControl = CameraCaptureControl(axis: .horizontal)
var shutterButtonLayoutGuide: UILayoutGuide { var shutterButtonLayoutGuide: UILayoutGuide {
captureControl.shutterButtonLayoutGuide captureControl.shutterButtonLayoutGuide
} }
override init(frame: CGRect) { override init(frame: CGRect) {
super.init(frame: frame) super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
layoutMargins = UIEdgeInsets(top: 0, leading: 12, bottom: 0, trailing: 14) layoutMargins = UIEdgeInsets(top: 0, leading: 12, bottom: 0, trailing: 14)
addLayoutGuide(controlButtonsLayoutGuide) addLayoutGuide(controlButtonsLayoutGuide)
addConstraints([ controlButtonsLayoutGuide.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor), addConstraints([ controlButtonsLayoutGuide.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
controlButtonsLayoutGuide.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor) ]) controlButtonsLayoutGuide.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor) ])
photoLibraryButton = MediaPickerThumbnailButton(frame: CGRect(origin: .zero, size: .square(bounds.height)))
photoLibraryButton.translatesAutoresizingMaskIntoConstraints = false photoLibraryButton.translatesAutoresizingMaskIntoConstraints = false
addSubview(photoLibraryButton) addSubview(photoLibraryButton)
addConstraints([ photoLibraryButton.leadingAnchor.constraint(equalTo: controlButtonsLayoutGuide.leadingAnchor), addConstraints([ photoLibraryButton.leadingAnchor.constraint(equalTo: controlButtonsLayoutGuide.leadingAnchor),
photoLibraryButton.centerYAnchor.constraint(equalTo: controlButtonsLayoutGuide.centerYAnchor), photoLibraryButton.centerYAnchor.constraint(equalTo: controlButtonsLayoutGuide.centerYAnchor),
photoLibraryButton.topAnchor.constraint(greaterThanOrEqualTo: controlButtonsLayoutGuide.topAnchor) ]) photoLibraryButton.topAnchor.constraint(greaterThanOrEqualTo: controlButtonsLayoutGuide.topAnchor) ])
switchCameraButton = CameraOverlayButton(image: ButtonImages.switchCamera, userInterfaceStyleOverride: .dark)
switchCameraButton.translatesAutoresizingMaskIntoConstraints = false switchCameraButton.translatesAutoresizingMaskIntoConstraints = false
addSubview(switchCameraButton) addSubview(switchCameraButton)
addConstraints([ switchCameraButton.trailingAnchor.constraint(equalTo: controlButtonsLayoutGuide.trailingAnchor), addConstraints([ switchCameraButton.trailingAnchor.constraint(equalTo: controlButtonsLayoutGuide.trailingAnchor),
switchCameraButton.topAnchor.constraint(greaterThanOrEqualTo: controlButtonsLayoutGuide.topAnchor), switchCameraButton.topAnchor.constraint(greaterThanOrEqualTo: controlButtonsLayoutGuide.topAnchor),
switchCameraButton.centerYAnchor.constraint(equalTo: controlButtonsLayoutGuide.centerYAnchor) ]) switchCameraButton.centerYAnchor.constraint(equalTo: controlButtonsLayoutGuide.centerYAnchor) ])
captureControl = CameraCaptureControl(axis: .horizontal)
captureControl.translatesAutoresizingMaskIntoConstraints = false captureControl.translatesAutoresizingMaskIntoConstraints = false
addSubview(captureControl) addSubview(captureControl)
captureControl.autoPinTopToSuperviewMargin() captureControl.autoPinTopToSuperviewMargin()
@ -849,6 +831,11 @@ private class BottomBar: UIView {
updateCompactHeightLayoutConstraints() updateCompactHeightLayoutConstraints()
} }
@available(*, unavailable, message: "Use init(frame:) instead")
required init?(coder: NSCoder) {
notImplemented()
}
private func updateCompactHeightLayoutConstraints() { private func updateCompactHeightLayoutConstraints() {
if isCompactHeightLayout { if isCompactHeightLayout {
removeConstraints(regularHeightLayoutConstraints) removeConstraints(regularHeightLayoutConstraints)
@ -869,49 +856,41 @@ private class SideBar: UIView {
} }
} }
private var cameraControlsContainerView: UIView! private let cameraControlsContainerView: UIStackView
private(set) var flashModeButton: CameraOverlayButton! let flashModeButton = CameraOverlayButton(image: ButtonImages.flashAuto, userInterfaceStyleOverride: .dark)
private(set) var batchModeButton: CameraOverlayButton! let batchModeButton = CameraOverlayButton(image: ButtonImages.batchModeOff, userInterfaceStyleOverride: .dark)
private(set) var switchCameraButton: CameraOverlayButton! let switchCameraButton = CameraOverlayButton(image: ButtonImages.switchCamera, userInterfaceStyleOverride: .dark)
private(set) var photoLibraryButton: MediaPickerThumbnailButton! let photoLibraryButton = MediaPickerThumbnailButton(frame: .zero)
private(set) var cameraCaptureControl = CameraCaptureControl(axis: .vertical) private(set) var cameraCaptureControl = CameraCaptureControl(axis: .vertical)
override init(frame: CGRect) { override init(frame: CGRect) {
cameraControlsContainerView = UIStackView(arrangedSubviews: [ batchModeButton, flashModeButton, switchCameraButton ])
super.init(frame: frame) super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
layoutMargins = UIEdgeInsets(margin: 8) layoutMargins = UIEdgeInsets(margin: 8)
flashModeButton = CameraOverlayButton(image: ButtonImages.flashAuto, userInterfaceStyleOverride: .dark) cameraControlsContainerView.spacing = 16
switchCameraButton = CameraOverlayButton(image: ButtonImages.switchCamera, userInterfaceStyleOverride: .dark) cameraControlsContainerView.axis = .vertical
batchModeButton = CameraOverlayButton(image: ButtonImages.batchModeOff, userInterfaceStyleOverride: .dark) addSubview(cameraControlsContainerView)
let stackView = UIStackView(arrangedSubviews: [ batchModeButton, flashModeButton, switchCameraButton ]) cameraControlsContainerView.autoPinWidthToSuperviewMargins()
stackView.spacing = 16 cameraControlsContainerView.autoPinTopToSuperviewMargin()
stackView.axis = .vertical
addSubview(stackView)
stackView.autoPinWidthToSuperviewMargins()
stackView.autoPinTopToSuperviewMargin()
cameraControlsContainerView = stackView
addSubview(cameraCaptureControl) addSubview(cameraCaptureControl)
cameraCaptureControl.autoHCenterInSuperview() cameraCaptureControl.autoHCenterInSuperview()
cameraCaptureControl.shutterButtonLayoutGuide.topAnchor.constraint(equalTo: stackView.bottomAnchor, constant: 36).isActive = true cameraCaptureControl.shutterButtonLayoutGuide.topAnchor.constraint(equalTo: cameraControlsContainerView.bottomAnchor, constant: 36).isActive = true
photoLibraryButton = MediaPickerThumbnailButton(frame: CGRect(origin: .zero, size: .square(bounds.height)))
addSubview(photoLibraryButton) addSubview(photoLibraryButton)
photoLibraryButton.autoHCenterInSuperview() photoLibraryButton.autoHCenterInSuperview()
photoLibraryButton.topAnchor.constraint(equalTo: cameraCaptureControl.shutterButtonLayoutGuide.bottomAnchor, constant: 36).isActive = true photoLibraryButton.topAnchor.constraint(equalTo: cameraCaptureControl.shutterButtonLayoutGuide.bottomAnchor, constant: 36).isActive = true
photoLibraryButton.bottomAnchor.constraint(lessThanOrEqualTo: layoutMarginsGuide.bottomAnchor).isActive = true photoLibraryButton.bottomAnchor.constraint(lessThanOrEqualTo: layoutMarginsGuide.bottomAnchor).isActive = true
}
@available(*, unavailable, message: "Use init(frame:) instead")
required init?(coder: NSCoder) {
notImplemented()
} }
} }
@ -1114,8 +1093,9 @@ class CapturePreviewView: UIView {
layer.addSublayer(previewLayer) layer.addSublayer(previewLayer)
} }
required init?(coder aDecoder: NSCoder) { @available(*, unavailable, message: "Use init(session:) instead")
fatalError("init(coder:) has not been implemented") required init?(coder: NSCoder) {
notImplemented()
} }
} }
@ -1123,15 +1103,7 @@ private class RecordingTimerView: PillView {
override init(frame: CGRect) { override init(frame: CGRect) {
super.init(frame: frame) super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
layoutMargins = UIEdgeInsets(hMargin: 16, vMargin: 0) layoutMargins = UIEdgeInsets(hMargin: 16, vMargin: 0)
let backgroundView = UIVisualEffectView(effect: UIBlurEffect(style: .dark)) let backgroundView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
@ -1148,9 +1120,14 @@ private class RecordingTimerView: PillView {
updateView() updateView()
} }
@available(*, unavailable, message: "Use init(frame:) instead")
required init?(coder: NSCoder) {
notImplemented()
}
// MARK: - Subviews // MARK: - Subviews
private lazy var label: UILabel = { private let label: UILabel = {
let label = UILabel() let label = UILabel()
label.font = UIFont.ows_monospacedDigitFont(withSize: 20) label.font = UIFont.ows_monospacedDigitFont(withSize: 20)
label.textAlignment = .center label.textAlignment = .center