You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
session-ios/Session/Shared/Views/SessionCell+AccessoryView.s...

376 lines
17 KiB
Swift

// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import UIKit
import SessionUIKit
import SessionMessagingKit
import SessionUtilitiesKit
import SignalUtilitiesKit
extension SessionCell {
public class AccessoryView: UIView {
// MARK: - UI
private lazy var imageViewConstraints: [NSLayoutConstraint] = [
imageView.pin(.top, to: .top, of: self),
imageView.pin(.leading, to: .leading, of: self),
imageView.pin(.trailing, to: .trailing, of: self),
imageView.pin(.bottom, to: .bottom, of: self)
]
private lazy var imageViewWidthConstraint: NSLayoutConstraint = imageView.set(.width, to: 0)
private lazy var imageViewHeightConstraint: NSLayoutConstraint = imageView.set(.height, to: 0)
private lazy var toggleSwitchConstraints: [NSLayoutConstraint] = [
toggleSwitch.pin(.top, to: .top, of: self),
toggleSwitch.pin(.leading, to: .leading, of: self),
toggleSwitch.pin(.trailing, to: .trailing, of: self),
toggleSwitch.pin(.bottom, to: .bottom, of: self)
]
private lazy var dropDownStackViewConstraints: [NSLayoutConstraint] = [
dropDownStackView.pin(.top, to: .top, of: self),
dropDownStackView.pin(.leading, to: .leading, of: self),
dropDownStackView.pin(.trailing, to: .trailing, of: self),
dropDownStackView.pin(.bottom, to: .bottom, of: self)
]
private lazy var radioViewWidthConstraint: NSLayoutConstraint = radioView.set(.width, to: 0)
private lazy var radioViewHeightConstraint: NSLayoutConstraint = radioView.set(.height, to: 0)
private lazy var radioBorderViewWidthConstraint: NSLayoutConstraint = radioBorderView.set(.width, to: 0)
private lazy var radioBorderViewHeightConstraint: NSLayoutConstraint = radioBorderView.set(.height, to: 0)
private lazy var radioBorderViewConstraints: [NSLayoutConstraint] = [
radioBorderView.pin(.top, to: .top, of: self),
radioBorderView.pin(.leading, to: .leading, of: self),
radioBorderView.pin(.trailing, to: .trailing, of: self),
radioBorderView.pin(.bottom, to: .bottom, of: self)
]
private lazy var highlightingBackgroundLabelConstraints: [NSLayoutConstraint] = [
highlightingBackgroundLabel.pin(.top, to: .top, of: self),
highlightingBackgroundLabel.pin(.leading, to: .leading, of: self),
highlightingBackgroundLabel.pin(.trailing, to: .trailing, of: self),
highlightingBackgroundLabel.pin(.bottom, to: .bottom, of: self)
]
private lazy var profilePictureViewConstraints: [NSLayoutConstraint] = [
profilePictureView.pin(.top, to: .top, of: self),
profilePictureView.pin(.leading, to: .leading, of: self),
profilePictureView.pin(.trailing, to: .trailing, of: self),
profilePictureView.pin(.bottom, to: .bottom, of: self)
]
private let imageView: UIImageView = {
let result: UIImageView = UIImageView()
result.translatesAutoresizingMaskIntoConstraints = false
result.clipsToBounds = true
result.contentMode = .scaleAspectFit
result.themeTintColor = .textPrimary
result.layer.minificationFilter = .trilinear
result.layer.magnificationFilter = .trilinear
result.isHidden = true
return result
}()
private let toggleSwitch: UISwitch = {
let result: UISwitch = UISwitch()
result.translatesAutoresizingMaskIntoConstraints = false
result.isUserInteractionEnabled = false // Triggered by didSelectCell instead
result.themeOnTintColor = .primary
result.isHidden = true
result.setContentHuggingHigh()
result.setCompressionResistanceHigh()
return result
}()
private let dropDownStackView: UIStackView = {
let result: UIStackView = UIStackView()
result.translatesAutoresizingMaskIntoConstraints = false
result.axis = .horizontal
result.distribution = .fill
result.alignment = .center
result.spacing = Values.verySmallSpacing
result.isHidden = true
return result
}()
private let dropDownImageView: UIImageView = {
let result: UIImageView = UIImageView(image: UIImage(systemName: "arrowtriangle.down.fill"))
result.translatesAutoresizingMaskIntoConstraints = false
result.themeTintColor = .textPrimary
result.set(.width, to: 10)
result.set(.height, to: 10)
return result
}()
private let dropDownLabel: UILabel = {
let result: UILabel = UILabel()
result.translatesAutoresizingMaskIntoConstraints = false
result.font = .systemFont(ofSize: Values.smallFontSize, weight: .medium)
result.themeTextColor = .textPrimary
result.setContentHuggingHigh()
result.setCompressionResistanceHigh()
return result
}()
private let radioBorderView: UIView = {
let result: UIView = UIView()
result.translatesAutoresizingMaskIntoConstraints = false
result.isUserInteractionEnabled = false
result.layer.borderWidth = 1
result.themeBorderColor = .radioButton_unselectedBorder
result.isHidden = true
return result
}()
private let radioView: UIView = {
let result: UIView = UIView()
result.translatesAutoresizingMaskIntoConstraints = false
result.isUserInteractionEnabled = false
result.themeBackgroundColor = .radioButton_unselectedBackground
result.isHidden = true
return result
}()
public lazy var highlightingBackgroundLabel: SessionHighlightingBackgroundLabel = {
let result: SessionHighlightingBackgroundLabel = SessionHighlightingBackgroundLabel()
result.translatesAutoresizingMaskIntoConstraints = false
result.isHidden = true
return result
}()
private lazy var profilePictureView: ProfilePictureView = {
let result: ProfilePictureView = ProfilePictureView()
result.translatesAutoresizingMaskIntoConstraints = false
result.size = Values.smallProfilePictureSize
result.isHidden = true
result.set(.width, to: Values.smallProfilePictureSize)
result.set(.height, to: Values.smallProfilePictureSize)
return result
}()
private var customView: UIView?
// MARK: - Initialization
override init(frame: CGRect) {
super.init(frame: frame)
setupViewHierarchy()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupViewHierarchy()
}
private func setupViewHierarchy() {
addSubview(imageView)
addSubview(toggleSwitch)
addSubview(dropDownStackView)
addSubview(radioBorderView)
addSubview(highlightingBackgroundLabel)
addSubview(profilePictureView)
dropDownStackView.addArrangedSubview(dropDownImageView)
dropDownStackView.addArrangedSubview(dropDownLabel)
radioBorderView.addSubview(radioView)
radioView.center(in: radioBorderView)
}
// MARK: - Content
func prepareForReuse() {
self.isHidden = true
imageView.image = nil
imageView.themeTintColor = .textPrimary
imageView.contentMode = .scaleAspectFit
dropDownImageView.themeTintColor = .textPrimary
dropDownLabel.text = ""
dropDownLabel.themeTextColor = .textPrimary
radioBorderView.themeBorderColor = .radioButton_unselectedBorder
radioView.themeBackgroundColor = .radioButton_unselectedBackground
highlightingBackgroundLabel.text = ""
highlightingBackgroundLabel.themeTextColor = .textPrimary
customView?.removeFromSuperview()
imageView.isHidden = true
toggleSwitch.isHidden = true
dropDownStackView.isHidden = true
radioBorderView.isHidden = true
radioView.alpha = 1
radioView.isHidden = true
highlightingBackgroundLabel.isHidden = true
profilePictureView.isHidden = true
imageViewWidthConstraint.isActive = false
imageViewHeightConstraint.isActive = false
imageViewConstraints.forEach { $0.isActive = false }
toggleSwitchConstraints.forEach { $0.isActive = false }
dropDownStackViewConstraints.forEach { $0.isActive = false }
radioViewWidthConstraint.isActive = false
radioViewHeightConstraint.isActive = false
radioBorderViewWidthConstraint.isActive = false
radioBorderViewHeightConstraint.isActive = false
radioBorderViewConstraints.forEach { $0.isActive = false }
highlightingBackgroundLabelConstraints.forEach { $0.isActive = false }
profilePictureViewConstraints.forEach { $0.isActive = false }
}
public func update(
with accessory: Accessory?,
tintColor: ThemeValue,
isEnabled: Bool,
accessibilityLabel: String?
) {
guard let accessory: Accessory = accessory else { return }
// If we have an accessory value then this shouldn't be hidden
self.isHidden = false
switch accessory {
case .icon(let image, let iconSize, let customTint, let shouldFill):
imageView.accessibilityLabel = accessibilityLabel
imageView.image = image
imageView.themeTintColor = (customTint ?? tintColor)
imageView.contentMode = (shouldFill ? .scaleAspectFill : .scaleAspectFit)
imageView.isHidden = false
switch iconSize {
case .fit:
imageView.sizeToFit()
imageViewWidthConstraint.constant = imageView.bounds.width
imageViewHeightConstraint.constant = imageView.bounds.height
default:
imageViewWidthConstraint.constant = iconSize.size
imageViewHeightConstraint.constant = iconSize.size
}
imageViewWidthConstraint.isActive = true
imageViewHeightConstraint.isActive = true
imageViewConstraints.forEach { $0.isActive = true }
case .iconAsync(let iconSize, let customTint, let shouldFill, let setter):
setter(imageView)
imageView.accessibilityLabel = accessibilityLabel
imageView.themeTintColor = (customTint ?? tintColor)
imageView.contentMode = (shouldFill ? .scaleAspectFill : .scaleAspectFit)
imageView.isHidden = false
switch iconSize {
case .fit:
imageView.sizeToFit()
imageViewWidthConstraint.constant = imageView.bounds.width
imageViewHeightConstraint.constant = imageView.bounds.height
default:
imageViewWidthConstraint.constant = iconSize.size
imageViewHeightConstraint.constant = iconSize.size
}
imageViewWidthConstraint.isActive = true
imageViewHeightConstraint.isActive = true
imageViewConstraints.forEach { $0.isActive = true }
case .toggle(let dataSource):
toggleSwitch.accessibilityLabel = accessibilityLabel
toggleSwitch.isHidden = false
toggleSwitch.isEnabled = isEnabled
toggleSwitchConstraints.forEach { $0.isActive = true }
let newValue: Bool = dataSource.currentBoolValue
if newValue != toggleSwitch.isOn {
toggleSwitch.setOn(newValue, animated: true)
}
case .dropDown(let dataSource):
dropDownLabel.accessibilityLabel = accessibilityLabel
dropDownLabel.text = dataSource.currentStringValue
dropDownStackView.isHidden = false
dropDownStackViewConstraints.forEach { $0.isActive = true }
case .radio(let size, let isSelectedRetriever, let storedSelection):
let isSelected: Bool = isSelectedRetriever()
let wasOldSelection: Bool = (!isSelected && storedSelection)
radioBorderView.isHidden = false
radioBorderView.themeBorderColor = (isSelected ?
.radioButton_selectedBorder :
.radioButton_unselectedBorder
)
radioBorderView.layer.cornerRadius = (size.borderSize / 2)
radioView.accessibilityLabel = accessibilityLabel
radioView.alpha = (wasOldSelection ? 0.3 : 1)
radioView.isHidden = (!isSelected && !storedSelection)
radioView.themeBackgroundColor = (isSelected || wasOldSelection ?
.radioButton_selectedBackground :
.radioButton_unselectedBackground
)
radioView.layer.cornerRadius = (size.selectionSize / 2)
radioViewWidthConstraint.constant = size.selectionSize
radioViewHeightConstraint.constant = size.selectionSize
radioBorderViewWidthConstraint.constant = size.borderSize
radioBorderViewHeightConstraint.constant = size.borderSize
radioViewWidthConstraint.isActive = true
radioViewHeightConstraint.isActive = true
radioBorderViewWidthConstraint.isActive = true
radioBorderViewHeightConstraint.isActive = true
radioBorderViewConstraints.forEach { $0.isActive = true }
case .highlightingBackgroundLabel(let title):
highlightingBackgroundLabel.accessibilityLabel = accessibilityLabel
highlightingBackgroundLabel.text = title
highlightingBackgroundLabel.themeTextColor = tintColor
highlightingBackgroundLabel.isHidden = false
highlightingBackgroundLabelConstraints.forEach { $0.isActive = true }
case .profile(let profileId, let profile):
profilePictureView.accessibilityLabel = accessibilityLabel
profilePictureView.update(
publicKey: profileId,
profile: profile,
threadVariant: .contact
)
profilePictureView.isHidden = false
profilePictureViewConstraints.forEach { $0.isActive = true }
case .customView(let viewGenerator):
let generatedView: UIView = viewGenerator()
generatedView.accessibilityLabel = accessibilityLabel
addSubview(generatedView)
generatedView.pin(.top, to: .top, of: self)
generatedView.pin(.leading, to: .leading, of: self)
generatedView.pin(.trailing, to: .trailing, of: self)
generatedView.pin(.bottom, to: .bottom, of: self)
self.customView?.removeFromSuperview() // Just in case
self.customView = generatedView
case .threadInfo: break
}
}
// MARK: - Interaction
func setHighlighted(_ highlighted: Bool, animated: Bool) {
highlightingBackgroundLabel.setHighlighted(highlighted, animated: animated)
}
func setSelected(_ selected: Bool, animated: Bool) {
highlightingBackgroundLabel.setSelected(selected, animated: animated)
}
}
}