| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428 |
- //
- // SelectMemberView.swift
- // TUIRoomKit
- //
- // Created by CY zhao on 2024/6/18.
- //
- import UIKit
- import RTCRoomEngine
- protocol SelectMemberViewDelegate: AnyObject {
- func selectView(_ selectView: SelectMemberView, didSearchWith searchText:String)
- func didBackButtonClicked(in selectView: SelectMemberView)
- func didExpandButtonClicked(in selectView: SelectMemberView)
- func didConfirmButtonClicked(in selectView: SelectMemberView )
- }
- class SelectMemberView: UIView {
- weak var delegate: SelectMemberViewDelegate?
- static let maxVisiableAvatars = 10
-
- private let navigationBar: UIView = {
- let view = UIView()
- return view
- }()
-
- let backButton: UIButton = {
- let button = UIButton()
- button.setImage(UIImage(named: "room_back_black", in: tuiRoomKitBundle(), compatibleWith: nil), for: .normal)
- button.backgroundColor = .clear
- return button
- }()
-
- let titleLabel: UILabel = {
- let label = UILabel()
- label.text = .selectMemberText
- label.font = UIFont.boldSystemFont(ofSize: 18)
- label.textAlignment = .center
- return label
- }()
-
- let label: UILabel = {
- let label = UILabel()
- label.textColor = UIColor.tui_color(withHex: "2B2E38")
- label.font = UIFont(name: "PingFangSC-Regular", size: 16)
- label.text = .selectMemberText
- label.sizeToFit()
- return label
- }()
-
- let searchBar: UISearchBar = {
- let searchBar = UISearchBar()
- searchBar.placeholder = .enterUserIdText
- searchBar.setBackgroundImage(UIImage(), for: .any, barMetrics: .default)
- return searchBar
- }()
-
- let searchControl: UIControl = {
- let view = UIControl()
- view.backgroundColor = .clear
- view.isHidden = true
- return view
- }()
-
- let tableView: UITableView = {
- let tableView = UITableView(frame: .zero, style: .plain)
- tableView.register(ContactCell.self, forCellReuseIdentifier: ContactCell.reuseIdentifier)
- if #available(iOS 15.0, *) {
- tableView.sectionHeaderTopPadding = 0
- }
- return tableView
- }()
-
- let bottomView: UIView = {
- let view = UIView()
- return view
- }()
-
- let selectedUserView: UICollectionView = {
- let layout = UICollectionViewFlowLayout()
- layout.scrollDirection = .horizontal
- layout.itemSize = CGSize(width: 32, height: 32)
- layout.minimumLineSpacing = 8
-
- let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
- collectionView.showsHorizontalScrollIndicator = false
- collectionView.register(AvatarCell.self, forCellWithReuseIdentifier: AvatarCell.reuseIdentifier)
- collectionView.backgroundColor = .clear
- return collectionView
- }()
-
- let expandButton: UIButton = {
- let button = UIButton(type: .custom)
- button.titleLabel?.font = UIFont(name: "PingFangSC-Regular", size: 14)
- button.setTitleColor(UIColor.tui_color(withHex: "#22262E"), for: .normal)
- button.setTitle(.selectedText, for: .normal)
- let normalIcon = UIImage(named: "room_up_black_arrow", in: tuiRoomKitBundle(), compatibleWith: nil)
- button.setImage(normalIcon, for: .normal)
- button.sizeToFit()
- button.isHidden = true
- return button
- }()
-
- let confirmButton: UIButton = {
- let button = UIButton()
- button.backgroundColor = UIColor.tui_color(withHex: "1C66E5")
- button.titleLabel?.font = UIFont(name: "PingFangSC-Medium", size: 14)
- button.setTitleColor(UIColor(0xFFFFFF), for: .normal)
- button.setTitle(.confirmText, for: .normal)
- button.titleLabel?.textAlignment = .center
- button.layer.cornerRadius = 16
- button.isEnabled = false
- button.contentEdgeInsets = UIEdgeInsets(top: 5, left: 24, bottom: 5, right: 24)
- button.sizeToFit()
- return button
- }()
-
- lazy var currentLanguage: String = {
- let locale = Locale.current
- let languageCode = locale.languageCode ?? "en"
- return languageCode
- }()
-
- override func layoutSubviews() {
- super.layoutSubviews()
- if let textField = searchBar.value(forKey: "searchField") as? UITextField {
- textField.layer.cornerRadius = textField.bounds.height / 2
- textField.clipsToBounds = true
- }
- }
-
- private var isViewReady: Bool = false
- override func didMoveToWindow() {
- super.didMoveToWindow()
- guard !isViewReady else { return }
- constructViewHierarchy()
- activateConstraints()
- bindInteraction()
- isViewReady = true
- }
-
- private func constructViewHierarchy() {
- addSubview(navigationBar)
- navigationBar.addSubview(backButton)
- navigationBar.addSubview(titleLabel)
- addSubview(searchBar)
- addSubview(tableView)
- addSubview(bottomView)
- bottomView.addSubview(selectedUserView)
- bottomView.addSubview(expandButton)
- bottomView.addSubview(confirmButton)
- addSubview(searchControl)
- }
-
- private func activateConstraints() {
- navigationBar.snp.makeConstraints { make in
- make.top.equalTo(safeAreaLayoutGuide.snp.top)
- make.leading.trailing.equalToSuperview()
- make.height.equalTo(50)
- }
- backButton.snp.makeConstraints { make in
- make.leading.equalToSuperview().offset(22)
- make.centerY.equalToSuperview()
- }
- titleLabel.snp.makeConstraints { make in
- make.centerX.equalToSuperview()
- make.centerY.equalToSuperview()
- }
- searchBar.snp.makeConstraints { make in
- make.leading.equalToSuperview().offset(16)
- make.trailing.equalToSuperview().offset(-16)
- make.top.equalTo(navigationBar.snp.bottom).offset(12)
- make.height.equalTo(42)
- }
- tableView.snp.makeConstraints { make in
- make.leading.trailing.equalToSuperview()
- make.top.equalTo(searchBar.snp.bottom).offset(16)
- make.bottom.equalTo(bottomView.snp.top)
- }
- bottomView.snp.makeConstraints { make in
- make.leading.trailing.bottom.equalToSuperview()
- make.height.equalTo(84)
- }
- confirmButton.snp.makeConstraints { make in
- make.trailing.equalToSuperview().offset(-16)
- make.top.equalToSuperview().offset(10)
- }
- selectedUserView.snp.makeConstraints { make in
- make.leading.equalToSuperview().offset(32)
- make.trailing.equalTo(confirmButton.snp.leading).offset(-16)
- make.top.equalToSuperview()
- make.height.equalTo(50)
- }
- expandButton.snp.makeConstraints { make in
- make.leading.equalToSuperview().offset(19)
- make.top.equalToSuperview().offset(16)
- make.width.greaterThanOrEqualTo(103)
- }
- searchControl.snp.makeConstraints { make in
- make.edges.equalToSuperview()
- }
- }
-
- private func bindInteraction() {
- backButton.addTarget(self, action: #selector(onBackButtonTapped(sender:)), for: .touchUpInside)
- expandButton.addTarget(self, action: #selector(onExpandButtonTapped(sender:)), for: .touchUpInside)
- confirmButton.addTarget(self, action: #selector(onConfirmButtonTapped(sender:)), for: .touchUpInside)
- searchBar.delegate = self
- let tap = UITapGestureRecognizer(target: self, action: #selector(hideSearchControl(sender:)))
- searchControl.addGestureRecognizer(tap)
- }
-
- @objc func hideSearchControl(sender: UIView) {
- if #available(iOS 13, *) {
- searchBar.searchTextField.resignFirstResponder()
- } else {
- searchBar.resignFirstResponder()
- }
- searchControl.isHidden = true
- }
-
- @objc func onBackButtonTapped(sender: UIButton) {
- delegate?.didBackButtonClicked(in: self)
- }
-
- @objc func onExpandButtonTapped(sender: UIButton) {
- delegate?.didExpandButtonClicked(in: self)
- }
-
- @objc func onConfirmButtonTapped(sender: UIButton) {
- delegate?.didConfirmButtonClicked(in: self)
- }
-
- func updateSelectedView(with count: Int) {
- if count <= SelectMemberView.maxVisiableAvatars {
- self.expandButton.isHidden = true
- self.selectedUserView.isHidden = false
- self.selectedUserView.reloadData()
- } else {
- self.expandButton.isHidden = false
- self.selectedUserView.isHidden = true
- self.updateExpandButton(with: count)
- }
- }
-
- func updateConfirmButton(with count: Int) {
- if count > 0 {
- confirmButton.isEnabled = true
- }
-
- if count <= SelectMemberView.maxVisiableAvatars && count > 0 {
- let text = .confirmText + "(" + "\(count)" + ")"
- confirmButton.setTitle(text, for: .normal)
- } else {
- confirmButton.setTitle(.confirmText, for: .normal)
- }
- }
-
- private func updateExpandButton(with count: Int) {
- var text = .selectedText + ": " + "\(count)"
- if currentLanguage == "zh" || currentLanguage == "zh-Hant" {
- text += "人"
- }
- expandButton.setTitle(text, for: .normal)
-
- let imageWidth = expandButton.imageView?.bounds.size.width ?? 0
- let titleWidth = expandButton.titleLabel?.bounds.size.width ?? 0
- let spacing: CGFloat = 4
- expandButton.titleEdgeInsets = UIEdgeInsets(top: 0,
- left: -imageWidth,
- bottom: 0,
- right: imageWidth + spacing);
- expandButton.imageEdgeInsets = UIEdgeInsets(top: 0,
- left: titleWidth + spacing,
- bottom: 0,
- right: -titleWidth)
- }
- }
- extension SelectMemberView: UISearchBarDelegate {
- func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
- let searchContentText = searchText.trimmingCharacters(in: .whitespaces)
- delegate?.selectView(self, didSearchWith: searchContentText)
- }
-
- func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
- hideSearchControl(sender: searchBar)
- }
-
- func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool {
- searchControl.isHidden = false
- return true
- }
- }
- class ContactCell: UITableViewCell {
- static let reuseIdentifier = "ContactCell"
- let checkBox: UIButton = {
- let button = UIButton(type: .custom)
- button.setImage(UIImage(named: "room_check_mark_unselect", in: tuiRoomKitBundle(), compatibleWith: nil), for: .normal)
- button.setImage(UIImage(named: "room_check_mark", in: tuiRoomKitBundle(), compatibleWith: nil), for: .selected)
- button.isUserInteractionEnabled = false
- return button
- }()
-
- let avatarImageView: UIImageView = {
- let imgView = UIImageView()
- imgView.layer.cornerRadius = 2
- imgView.layer.masksToBounds = true
- return imgView
- }()
-
- let nameLabel: UILabel = {
- let label = UILabel()
- label.textColor = UIColor.tui_color(withHex: "22262E")
- label.textAlignment = .left
- label.font = UIFont(name: "PingFangSC-Regular", size: 14)
- label.numberOfLines = 1
- return label
- }()
-
- private var isViewReady = false
- override func didMoveToWindow() {
- super.didMoveToWindow()
- guard !isViewReady else {
- return
- }
- isViewReady = true
- selectionStyle = .none
- constructViewHierarchy()
- activateConstraints()
- contentView.backgroundColor = .clear
- }
-
- private func constructViewHierarchy() {
- contentView.addSubview(checkBox)
- contentView.addSubview(avatarImageView)
- contentView.addSubview(nameLabel)
- }
-
- private func activateConstraints() {
- checkBox.snp.makeConstraints { make in
- make.leading.equalToSuperview().offset(20)
- make.centerY.equalToSuperview()
- make.width.height.equalTo(16)
- }
- avatarImageView.snp.makeConstraints { make in
- make.leading.equalTo(checkBox.snp.trailing).offset(6)
- make.centerY.equalToSuperview()
- make.width.height.equalTo(32)
- }
- nameLabel.snp.makeConstraints { make in
- make.leading.equalTo(avatarImageView.snp.trailing).offset(6)
- make.centerY.equalToSuperview()
- }
- }
-
- func setupViewState(with info: User, isSelected: Bool, isDisaled: Bool) {
- let placeholder = UIImage(named: "room_default_avatar_rect", in: tuiRoomKitBundle(), compatibleWith: nil)
- if let url = URL(string: info.avatarUrl) {
- avatarImageView.sd_setImage(with: url, placeholderImage: placeholder)
- } else {
- avatarImageView.image = placeholder
- }
-
- if !info.userName.isEmpty {
- nameLabel.text = info.userName
- } else {
- nameLabel.text = info.userId
- }
- checkBox.isSelected = isSelected || isDisaled
-
- if isDisaled {
- contentView.alpha = 0.5
- } else {
- contentView.alpha = 1
- }
- }
- }
- class AvatarCell: UICollectionViewCell {
- static let reuseIdentifier = "AvatarCell"
- let imageView: UIImageView = {
- let imageView = UIImageView()
- imageView.contentMode = .scaleAspectFill
- imageView.clipsToBounds = true
- imageView.layer.cornerRadius = 2
- return imageView
- }()
-
- private var isViewReady = false
- override func didMoveToWindow() {
- super.didMoveToWindow()
- guard !isViewReady else {
- return
- }
- isViewReady = true
- constructViewHierarchy()
- activateConstraints()
- contentView.backgroundColor = .clear
- }
-
- private func constructViewHierarchy() {
- contentView.addSubview(imageView)
- }
-
- private func activateConstraints() {
- imageView.snp.makeConstraints { make in
- make.top.leading.bottom.trailing.equalTo(contentView)
- }
- }
-
- func setupViewState(with info: User) {
- let placeholder = UIImage(named: "room_default_avatar_rect", in: tuiRoomKitBundle(), compatibleWith: nil)
- if let url = URL(string: info.avatarUrl) {
- imageView.sd_setImage(with: url, placeholderImage: placeholder)
- } else {
- imageView.image = placeholder
- }
- }
- }
- private extension String {
- static let selectMemberText = localized("Select Members")
- static let enterUserIdText = localized("Enter userID or username")
- static let confirmText = localized("OK")
- static let selectedText = localized("Selected")
- }
|