removeFromSuperView

我测试了一下,取消掉 super.tpbMakeConstraint()的注释,所有的view都能被加载,但是设备列表、scrollview、searchbar都叠到了一起,请仔细分析重新生成完整代码 import UIKit import SnapKit class DeviceListNewViewController: SurveillanceCommonTableController { // MARK: - 属性 private lazy var titleContainerView: UIView = { let view = UIView() view.backgroundColor = .red return view }() private lazy var titleLabel: UILabel = { let label = UILabel() label.text = "设备列表" label.font = .tpm20Medium() label.textColor = .tpbTextPrimary label.textAlignment = .center label.numberOfLines = 1 return label }() private lazy var searchBar: TPBSearchBar = { let bar = TPBSearchBar() bar.placeholder = "搜索" bar.backgroundColor = .clear bar.delegate = self return bar }() private lazy var searchContainerView: UIView = { let view = UIView() view.backgroundColor = UIColor(white: 0.96, alpha: 1.0) view.layer.cornerRadius = 22 view.clipsToBounds = true view.addSubview(searchBar) return view }() private lazy var tabScrollView: UIScrollView = { let scrollView = UIScrollView() scrollView.showsHorizontalScrollIndicator = false scrollView.backgroundColor = .clear scrollView.translatesAutoresizingMaskIntoConstraints = false return scrollView }() private lazy var tabButtons: [UIButton] = { return tabButtonTitles.enumerated().map { index, title in let btn = UIButton(type: .custom) btn.tag = index btn.setTitle(title, for: .normal) btn.titleLabel?.font = UIFont.tpr14Regular() btn.setTitleColor(.black, for: .normal) btn.setTitleColor(.tpbPrimary, for: .selected) btn.layer.borderWidth = 1 btn.layer.borderColor = UIColor(white: 0.85, alpha: 1.0).cgColor btn.layer.cornerRadius = 20 btn.contentEdgeInsets = UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16) btn.addTarget(self, action: #selector(onTabTapped(_:)), for: .touchUpInside) return btn } }() private let tabButtonTitles = ["所有设备", "收藏页面", "站点选择"] private var selectedTabIndex = 0 // MARK: - UICollectionView private var collectionView: UICollectionView! // MARK: - View Setup override func tpbSetupSubviews() { super.tpbSetupSubviews() setupNav() setupCollectionView() view.addSubviews([ titleContainerView, searchContainerView, tabScrollView, tableView ]) titleContainerView.addSubview(titleLabel) searchContainerView.addSubview(searchBar) updateTabSelection(index: selectedTabIndex) layoutTabButtonsInScrollView() } private func setupNav() { navigationItem.leftBarButtonItem = tpbCreateLeftBarButtonItem( with: TPImageLiteral("icon_back"), andTarget: self, andAction: #selector(onBack) ) navigationItem.rightBarButtonItem = tpbCreateRightBarButtonItem( with: TPImageLiteral("icon_settings"), andTarget: self, andAction: #selector(onSettings) ) navigationItem.title = "" } private func setupCollectionView() { let layout = UICollectionViewFlowLayout() layout.scrollDirection = .vertical layout.minimumLineSpacing = 10 layout.minimumInteritemSpacing = 10 layout.sectionInset = UIEdgeInsets(top: 10, left: 16, bottom: 10, right: 16) collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) collectionView.backgroundColor = .tpbBackground collectionView.alwaysBounceVertical = true collectionView.layer.cornerRadius = 12 collectionView.clipsToBounds = true // 注册 cell(假设使用默认 UICollectionViewCell) collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "DeviceCell") } override func tpbMakeConstraint() { super.tpbMakeConstraint() titleContainerView.snp.makeConstraints { make in make.top.equalTo(view.safeAreaLayoutGuide).offset(8) make.leading.trailing.equalToSuperview().inset(16) make.height.equalTo(40) } titleLabel.snp.makeConstraints { make in make.center.equalToSuperview() make.leading.greaterThanOrEqualToSuperview().offset(20) make.trailing.lessThanOrEqualToSuperview().offset(-20) } searchContainerView.snp.makeConstraints { make in make.top.equalTo(titleContainerView.snp.bottom).offset(16) make.leading.trailing.equalToSuperview().inset(16) make.height.equalTo(44) } searchBar.snp.makeConstraints { make in make.edges.equalTo(searchContainerView).inset(UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 16)) } tabScrollView.snp.makeConstraints { make in make.top.equalTo(searchContainerView.snp.bottom).offset(16) make.leading.trailing.equalToSuperview().inset(0) make.height.equalTo(40) } tableView.snp.remakeConstraints { make in make.top.equalTo(tabScrollView.snp.bottom) make.leading.trailing.equalToSuperview() make.bottom.equalTo(view.safeAreaLayoutGuide) } } // MARK: - 内联布局 Tab 按钮 private func layoutTabButtonsInScrollView() { tabScrollView.subviews.forEach { $0.removeFromSuperview() } var previousButton: UIButton? for button in tabButtons { tabScrollView.addSubview(button) button.snp.makeConstraints { make in make.centerY.equalToSuperview() make.height.equalTo(36) if let prev = previousButton { make.leading.equalTo(prev.snp.trailing).offset(8) } else { make.leading.equalTo(tabScrollView.contentLayoutGuide).offset(16) } make.width.greaterThanOrEqualTo(80) } previousButton = button } // 手动设置 contentSize DispatchQueue.main.async { var totalWidth: CGFloat = 16 for (i, btn) in self.tabButtons.enumerated() { totalWidth += btn.intrinsicContentSize.width + (i > 0 ? 8 : 0) } totalWidth += 16 self.tabScrollView.contentSize = CGSize(width: totalWidth, height: 40) } } // MARK: - 数据模型构建:现在把 UICollectionView 放入 cell private func reloadData() { var tempSectionArray = [TPBTableSectionModel]() // Section 0: 包含 titleView(已添加到主 view)、searchBar、tabScrollView let section0 = TPBTableSectionModel() var cellModels = [TPBBaseTableCellModel]() // Cell 1: Tab ScrollView(横向滚动按钮组) let tabCellModel = TPBBaseTableCellModel.customContent(with: tabScrollView) cellModels.append(tabCellModel) // Section 0 结束 section0.cellModelArray = cellModels tempSectionArray.append(section0) // Section 1: 真实的 UICollectionView let section1 = TPBTableSectionModel() let collectionCellModel = TPBBaseTableCellModel.customContent(with: collectionView) collectionCellModel.height = TPBTableElementHeight.customHeight(500) // 固定高度,内部可滚动 section1.cellModelArray = [collectionCellModel] tempSectionArray.append(section1) // 赋值并刷新 sectionArray = tempSectionArray tableView.reloadData() } // MARK: - 生命周期 override func viewDidLoad() { super.viewDidLoad() reloadData() // 绑定数据源(必须在 reloadData 后确保 collectionView 已加入视图层级) collectionView.dataSource = self collectionView.delegate = self } // MARK: - 点击事件 @objc private func onBack() { navigationController?.popViewController(animated: true) } @objc private func onSettings() { print("Settings tapped") } @objc private func onTabTapped(_ sender: UIButton) { guard sender.tag != selectedTabIndex else { return } tabButtons[selectedTabIndex].isSelected = false selectedTabIndex = sender.tag sender.isSelected = true print("Switched to tab: \(selectedTabIndex)") // TODO: 切换 tab 时刷新 collectionView 数据 } private func updateTabSelection(index: Int) { for (i, btn) in tabButtons.enumerated() { btn.isSelected = (i == index) } } } // MARK: - UICollectionViewDataSource extension DeviceListNewViewController: UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 20 // 模拟数据 } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "DeviceCell", for: indexPath) cell.backgroundColor = .systemBlue return cell } } // MARK: - UICollectionViewDelegateFlowLayout extension DeviceListNewViewController: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let width = (collectionView.bounds.width - 32 - 10) / 2 // 两列,间距10 return CGSize(width: width, height: 80) } } // MARK: - TPBSearchBarDelegate extension DeviceListNewViewController: TPBSearchBarDelegate { func searchBar(_ searchBar: TPBSearchBar, textDidChange searchText: String) { // 可在此触发搜索过滤逻辑 } func searchBarTextDidBeginEditing(_ searchBar: TPBSearchBar) { addTapToDismissKeyboard() } func searchBarTextDidEndEditing(_ searchBar: TPBSearchBar) { removeTapToDismissKeyboard() } func searchBarSearchButtonClicked(_ searchBar: TPBSearchBar) { searchBar.resignFirstResponder() } } // MARK: - Keyboard Dismiss Gesture private extension DeviceListNewViewController { func addTapToDismissKeyboard() { let tap = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard)) tap.cancelsTouchesInView = false view.addGestureRecognizer(tap) objc_setAssociatedObject( self, &AssociatedKeys.searchBarTapGestureKey, tap, .OBJC_ASSOCIATION_RETAIN_NONATOMIC ) } func removeTapToDismissKeyboard() { guard let tap = objc_getAssociatedObject(self, &AssociatedKeys.searchBarTapGestureKey) as? UITapGestureRecognizer else { return } view.removeGestureRecognizer(tap) objc_setAssociatedObject(self, &AssociatedKeys.searchBarTapGestureKey, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } @objc func dismissKeyboard() { searchBar.resignFirstResponder() } } // MARK: - Associated Object Keys private struct AssociatedKeys { static var searchBarTapGestureKey: UInt8 = 0 } 同时我再发给你另一个同组件库的代码参考:import UIKit class AddOrganizationCheckDetailViewController: SurveillanceCommonTableController { // MARK: - 属性 private lazy var segmentControl: TPBSegmentedControl = { let segment = TPBSegmentedControl(segmentedStyle: .roundRect, itemArray: [LocalizedString(key: addOrganizationEssentialOrganization),LocalizedString(key: addOrganizationStandardOrganization)], initialIndex: 0) segment.indexDidChangeCallback = { [weak self] _ in self?.reloadView() } return segment }() private lazy var checkDetailView: AddOrganizationCheckDetailView = { let view = AddOrganizationCheckDetailView() view.updateWith(orgCategory: .essential) return view }() convenience init(orgCategory: TPSSVMSOrgCategory) { self.init() if orgCategory == .essential { self.segmentControl.selectedIndex = 0 } else { self.segmentControl.selectedIndex = 1 } } deinit { print("AddOrganizationCheckDetailViewController deinit") } // MARK: - View override func tpbSetupSubviews() { super.tpbSetupSubviews() setupNav() view.addSubview(segmentControl) } private func setupNav() { navigationItem.leftBarButtonItem = nil navigationItem.rightBarButtonItem = tpbCreateRightBarButtonItem(with: TPImageLiteral("common_close"), andTarget: self, andAction: #selector(clickBack)) navigationItem.title = LocalizedString(key: addOrganizationSelectOrgDetail) } override func tpbMakeConstraint() { // 暂不执行super不然会显示不了segment // super.tpbMakeConstraint() segmentControl.snp.makeConstraints { make in make.top.equalTo(view.safeAreaLayoutGuide).offset(8) make.leading.equalTo(view.safeAreaLayoutGuide).offset(16) make.trailing.equalTo(view.safeAreaLayoutGuide).offset(-16) make.height.equalTo(32) } tableView.snp.remakeConstraints { make in make.top.equalTo(segmentControl.snp.bottom) make.leading.trailing.equalTo(view.safeAreaLayoutGuide) make.bottom.equalTo(view) } } override func viewDidLoad() { super.viewDidLoad() reloadView() } private func reloadView() { reloadData() } // MARK: - dateModel private func reloadData() { var tempSectionArray = [TPBTableSectionModel]() // section0 let section0 = TPBTableSectionModel() var section0CellModelArray = [TPBBaseTableCellModel]() if segmentControl.selectedIndex == 0 { checkDetailView.updateWith(orgCategory: .essential) } else { checkDetailView.updateWith(orgCategory: .standard) } let detailCellModel = TPBBaseTableCellModel.customContent(with: checkDetailView) section0CellModelArray.append(detailCellModel) section0.cellModelArray = section0CellModelArray tempSectionArray.append(section0) sectionArray = tempSectionArray } // MARK: - 点击事件 @objc private func clickBack() { dismiss(animated: true) } } // 自定义view fileprivate class AddOrganizationCheckDetailLabel: TPBBaseView { // MARK: - 属性 private lazy var dotImageView: UIImageView = { let view = UIImageView(image: TPImageLiteral("thirdParty_dot_gray")) view.contentMode = .scaleAspectFit return view }() private lazy var titleLabel: UILabel = { let label = UILabel() label.numberOfLines = 0 label.font = .tpr14Regular() label.textColor = .tpbTextSecondaryContent label.textAlignment = .left label.lineBreakMode = .byWordWrapping return label }() // MARK: - View override func setupSubviews() { super.setupSubviews() backgroundColor = .clear addSubviews([dotImageView, titleLabel]) } override func makeConstraint() { super.makeConstraint() dotImageView.snp.makeConstraints { make in make.leading.equalToSuperview() make.top.equalToSuperview().offset(4) make.height.width.equalTo(8) } titleLabel.snp.makeConstraints { make in make.leading.equalTo(dotImageView.snp.trailing).offset(8) make.top.trailing.bottom.equalToSuperview() } } // MARK: - 更新函数 func updateWith(orgCategory: TPSSVMSOrgCategory, title: String) { if orgCategory == .essential { dotImageView.image = TPImageLiteral("thirdParty_dot_gray") } else { dotImageView.image = TPImageLiteral("number_diot") } titleLabel.text = title } } fileprivate class AddOrganizationCheckDetailView: TPBBaseView { // MARK: - 属性 private lazy var containerView: UIView = { let view = UIView() view.backgroundColor = .tpbCard view.layer.cornerRadius = 10 view.layer.masksToBounds = true return view }() private lazy var gradientLayer: CAGradientLayer = { // 加入底色 let layer = CAGradientLayer() layer.frame = CGRectMake(containerView.bounds.minX, containerView.bounds.minY, containerView.bounds.size.width, 112) layer.startPoint = CGPoint(x: 0, y: 0) layer.endPoint = CGPoint(x: 0, y: 1) layer.colors = [UIColor.tpbGreenLight.cgColor, UIColor.clear.cgColor] layer.isHidden = true return layer }() private lazy var rightLabelContainerView: UIView = { let view = UIView() view.backgroundColor = .tpbBlueLight view.layer.cornerRadius = 3 view.layer.maskedCorners = [.layerMinXMaxYCorner] return view }() private lazy var rightLabel: UILabel = { let label = UILabel() label.text = LocalizedString(key: addOrganizationSelectOrgFree) label.numberOfLines = 1 label.font = .tpr14Regular() label.textColor = .tpbBlue label.textAlignment = .left label.lineBreakMode = .byWordWrapping return label }() private lazy var titleLabel: UILabel = { let label = UILabel() label.numberOfLines = 0 label.font = .tpm20Medium() label.textColor = .tpbTextPrimary label.textAlignment = .center label.lineBreakMode = .byWordWrapping return label }() private lazy var subTitleLabel: UILabel = { let label = UILabel() label.text = LocalizedString(key: addOrganizationSelectOrgLicenseNeeded) label.numberOfLines = 0 label.font = .tpr16Regular() label.textColor = .tpbTextPrimary label.textAlignment = .center label.lineBreakMode = .byWordWrapping return label }() private lazy var networkImageView: UIImageView = { let view = UIImageView(image: TPImageLiteral("central_network_dis")) view.contentMode = .scaleAspectFit return view }() private lazy var networkLabel: UILabel = { let label = UILabel() label.text = LocalizedString(key: addOrganizationOmadaNetwork) label.numberOfLines = 0 label.font = .tpm16Medium() label.textColor = .tpbTextPrimary label.textAlignment = .left label.lineBreakMode = .byWordWrapping return label }() private lazy var guardImageView: UIImageView = { let view = UIImageView(image: TPImageLiteral("central_surveillance_dis")) view.contentMode = .scaleAspectFit return view }() private lazy var guardLabel: UILabel = { let label = UILabel() label.text = LocalizedString(key: shareSocialPlatformWebDetail) label.numberOfLines = 0 label.font = .tpm16Medium() label.textColor = .tpbTextPrimary label.textAlignment = .left label.lineBreakMode = .byWordWrapping return label }() private lazy var essentialPart1LabelList: [AddOrganizationCheckDetailLabel] = { var list = [AddOrganizationCheckDetailLabel]() for str in [LocalizedString(key: addOrganizationSelectOrgDetailPart1_Tip1), LocalizedString(key: addOrganizationSelectOrgDetailPart1_Tip2)] { let view = AddOrganizationCheckDetailLabel() view.updateWith(orgCategory: .essential, title: str) list.append(view) } return list }() private lazy var essentialPart2LabelList: [AddOrganizationCheckDetailLabel] = { var list = [AddOrganizationCheckDetailLabel]() for str in [LocalizedString(key: addOrganizationSelectOrgDetailNetwork_Essential_Tip1), LocalizedString(key: addOrganizationSelectOrgDetailNetwork_Essential_Tip2), LocalizedString(key: addOrganizationSelectOrgDetailNetwork_Essential_Tip3), LocalizedString(key: addOrganizationSelectOrgDetailNetwork_Essential_Tip4), LocalizedString(key: addOrganizationSelectOrgDetailNetwork_Essential_Tip5)] { let view = AddOrganizationCheckDetailLabel() view.updateWith(orgCategory: .essential, title: str) list.append(view) } return list }() private lazy var essentialPart3LabelList: [AddOrganizationCheckDetailLabel] = { var list = [AddOrganizationCheckDetailLabel]() for str in [LocalizedString(key: addOrganizationSelectOrgDetailGuard_Essential_Tip1), LocalizedString(key: addOrganizationSelectOrgDetailGuard_Essential_Tip2), LocalizedString(key: addOrganizationSelectOrgDetailGuard_Essential_Tip3), LocalizedString(key: addOrganizationSelectOrgDetailGuard_Essential_Tip4), LocalizedString(key: addOrganizationSelectOrgDetailGuard_Essential_Tip5), LocalizedString(key: addOrganizationSelectOrgDetailGuard_Essential_Tip6), LocalizedString(key: addOrganizationSelectOrgDetailGuard_Essential_Tip7)] { let view = AddOrganizationCheckDetailLabel() view.updateWith(orgCategory: .essential, title: str) list.append(view) } return list }() private lazy var standardPart1LabelList: [AddOrganizationCheckDetailLabel] = { var list = [AddOrganizationCheckDetailLabel]() for str in [LocalizedString(key: addOrganizationSelectOrgDetailPart1_Tip1), LocalizedString(key: addOrganizationSelectOrgDetailPart1_Tip2)] { let view = AddOrganizationCheckDetailLabel() view.updateWith(orgCategory: .standard, title: str) list.append(view) } return list }() private lazy var standardPart2LabelList: [AddOrganizationCheckDetailLabel] = { var list = [AddOrganizationCheckDetailLabel]() for str in [LocalizedString(key: addOrganizationSelectOrgDetailNetwork_Standard_Tip1), LocalizedString(key: addOrganizationSelectOrgDetailNetwork_Standard_Tip2), LocalizedString(key: addOrganizationSelectOrgDetailNetwork_Standard_Tip3), LocalizedString(key: addOrganizationSelectOrgDetailNetwork_Standard_Tip4), LocalizedString(key: addOrganizationSelectOrgDetailNetwork_Standard_Tip5)] { let view = AddOrganizationCheckDetailLabel() view.updateWith(orgCategory: .standard, title: str) list.append(view) } return list }() private lazy var standardPart3LabelList: [AddOrganizationCheckDetailLabel] = { var list = [AddOrganizationCheckDetailLabel]() for str in [LocalizedString(key: addOrganizationSelectOrgDetailGuard_Standard_Tip1), LocalizedString(key: addOrganizationSelectOrgDetailGuard_Standard_Tip2), LocalizedString(key: addOrganizationSelectOrgDetailGuard_Standard_Tip3)] { let view = AddOrganizationCheckDetailLabel() view.updateWith(orgCategory: .standard, title: str) list.append(view) } return list }() private lazy var part1StackView: UIStackView = { let view = UIStackView() view.axis = .vertical view.alignment = .leading view.spacing = 12 view.distribution = .equalSpacing return view }() private lazy var part2StackView: UIStackView = { let view = UIStackView() view.axis = .vertical view.alignment = .leading view.spacing = 12 view.distribution = .equalSpacing return view }() private lazy var part3StackView: UIStackView = { let view = UIStackView() view.axis = .vertical view.alignment = .leading view.spacing = 12 view.distribution = .equalSpacing return view }() // MARK: - View override func setupSubviews() { super.setupSubviews() addSubview(containerView) containerView.addSubviews([rightLabelContainerView, titleLabel, subTitleLabel, networkImageView, networkLabel, guardImageView, guardLabel, part1StackView, part2StackView, part3StackView]) rightLabelContainerView.addSubview(rightLabel) containerView.layer.addSublayer(gradientLayer) } override func makeConstraint() { super.makeConstraint() containerView.snp.remakeConstraints { make in make.edges.equalToSuperview() } rightLabelContainerView.snp.remakeConstraints { make in make.trailing.top.equalToSuperview() } rightLabel.snp.remakeConstraints { make in make.leading.equalToSuperview().offset(13) make.trailing.equalToSuperview().offset(-13) make.top.equalToSuperview().offset(2) make.bottom.equalToSuperview().offset(-2) } titleLabel.snp.remakeConstraints { make in make.leading.equalToSuperview().offset(16) make.trailing.equalToSuperview().offset(-16) make.top.equalToSuperview().offset(44) } subTitleLabel.snp.remakeConstraints { make in make.leading.trailing.equalTo(titleLabel) make.top.equalTo(titleLabel.snp.bottom).offset(8) } part1StackView.snp.remakeConstraints { make in make.leading.trailing.equalTo(titleLabel) if subTitleLabel.isHidden { make.top.equalTo(titleLabel.snp.bottom).offset(24) } else { make.top.equalTo(subTitleLabel.snp.bottom).offset(24) } } networkImageView.snp.remakeConstraints { make in make.leading.equalTo(titleLabel) make.centerY.equalTo(networkLabel) make.height.width.equalTo(18) } networkLabel.snp.remakeConstraints { make in make.leading.equalTo(networkImageView.snp.trailing).offset(8) make.trailing.equalTo(titleLabel) make.top.equalTo(part1StackView.snp.bottom).offset(24) } part2StackView.snp.remakeConstraints { make in make.leading.trailing.equalTo(titleLabel) make.top.equalTo(networkLabel.snp.bottom).offset(16) } guardImageView.snp.remakeConstraints { make in make.leading.equalTo(titleLabel) make.centerY.equalTo(guardLabel) make.height.width.equalTo(18) } guardLabel.snp.remakeConstraints { make in make.leading.equalTo(guardImageView.snp.trailing).offset(8) make.trailing.equalTo(titleLabel) make.top.equalTo(part2StackView.snp.bottom).offset(24) } part3StackView.snp.remakeConstraints { make in make.leading.trailing.equalTo(titleLabel) make.top.equalTo(guardLabel.snp.bottom).offset(16) make.bottom.equalToSuperview().offset(-24) } } // MARK: - 更新函数 func updateWith(orgCategory: TPSSVMSOrgCategory) { if orgCategory == .essential { gradientLayer.isHidden = true rightLabel.text = LocalizedString(key: addOrganizationSelectOrgFree) rightLabel.textColor = .tpbBlue rightLabelContainerView.backgroundColor = .tpbBlueLight titleLabel.text = LocalizedString(key: orgCategoryEssentials) subTitleLabel.isHidden = true networkImageView.image = TPImageLiteral("central_network_dis") guardImageView.image = TPImageLiteral("central_surveillance_dis") } else { gradientLayer.isHidden = false rightLabel.text = LocalizedString(key: addOrganizationSelectOrgLicense) rightLabel.textColor = .tpbTextWhite rightLabelContainerView.backgroundColor = .tpbPrimary titleLabel.text = LocalizedString(key: deviceSettingSmartWhiteLampModeAutoWTL) subTitleLabel.isHidden = false networkImageView.image = TPImageLiteral("central_network") guardImageView.image = TPImageLiteral("central_surveillance") } configStackView(orgCategory: orgCategory) makeConstraint() } override func layoutSubviews() { super.layoutSubviews() gradientLayer.frame = CGRectMake(containerView.bounds.minX, containerView.bounds.minY, containerView.bounds.size.width, 112) } private func configStackView(orgCategory: TPSSVMSOrgCategory) { for view in essentialPart1LabelList { view.removeFromSuperview() } for view in essentialPart2LabelList { view.removeFromSuperview() } for view in essentialPart3LabelList { view.removeFromSuperview() } for view in standardPart1LabelList { view.removeFromSuperview() } for view in standardPart2LabelList { view.removeFromSuperview() } for view in standardPart3LabelList { view.removeFromSuperview() } if orgCategory == .essential { for view in essentialPart1LabelList { part1StackView.addArrangedSubview(view) } for view in essentialPart2LabelList { part2StackView.addArrangedSubview(view) } for view in essentialPart3LabelList { part3StackView.addArrangedSubview(view) } } else { for view in standardPart1LabelList { part1StackView.addArrangedSubview(view) } for view in standardPart2LabelList { part2StackView.addArrangedSubview(view) } for view in standardPart3LabelList { part3StackView.addArrangedSubview(view) } } } }
最新发布
12-02
下面我实现的一个页面调用TABAnimated库实现骨架屏的代码(只有前半部分)与TABAnimated库的说明,现在我想规范这个代码,实现一个TPBSkeletonManager封装这些操作,一个封装TABAnimated库。提供自动生成、自定义样式骨架、默认样式骨架的骨架屏模组,使用和维护便捷,可以传入需要实现骨架屏的view、tableview、collection view,就能返回默认的骨架生成方法。只需要简单命令就能在骨架屏需要展示的时候启动对应的骨架屏,在骨架屏需要消失的时候也是一个明确简单的命令就能关闭骨架屏。 一个页面调用TABAnimated库实现骨架屏的代码: @implementation SDNV6SiteDashboardViewController - (instancetype)init { self = [super init]; if (self) { [self bindDelegate]; } return self; } // 目前直接对加载到Cell的UIView构建骨架屏并按顺序直接画出遮挡 // 对于无法加载识别的自定义view:SDNV6SiteDashboardDensityView *apDensityContainerView无法应用也难以快速修改 - 解决,使用手动方案一 // 对于没有声明在主程序的view:SDNInsightHeadView *headView,其“Wi-Fi Activity”也无法解决 - 尚未解决 - (void)viewDidLoad { // 骨架屏bug:新增页面部署骨架屏不然整体消失速度异常 self.WifiActivityHeadView = [[SDNInsightHeadView alloc] init]; // 正常页面生成 [super viewDidLoad]; [self updatePermission]; [self.tableView layoutIfNeeded]; [self reloadView]; [self omdRegisterForSkipLifeCycleEvent]; // 对每个业务 view 设置骨架动画 // self.deviceOverView.tabAnimated = [[TABViewAnimated alloc] init]; self.networkActivityView.tabAnimated = [[TABViewAnimated alloc] init]; self.WifiActivityHeadView.tabAnimated = [[TABViewAnimated alloc] init]; self.wifiActivityChartView.tabAnimated = [[TABViewAnimated alloc] init]; self.apDensityContainerView.tabAnimated = [[TABViewAnimated alloc] init]; // // 对无法识别的业务view和错误使用空白占位view辅助识别 // SDNV6SiteDashboardDensitySkeletonView *skeletonView = [[SDNV6SiteDashboardDensitySkeletonView alloc] init]; // // // 添加到wifiView // [self.wifiActivityChartView addSubview:skeletonView]; // // 设置约束覆盖整个容器 // [skeletonView mas_makeConstraints:^(MASConstraintMaker *make) { // make.edges.equalTo(self.wifiActivityChartView); // }]; // 启动骨架动画 // [self.wifiActivityChartView layoutIfNeeded]; // skeletonView.tabAnimated = [[TABViewAnimated alloc] init]; // 调整骨架屏view // deviceView调整:只要启动回调,整个页面逻辑都会被破坏,TABA库的应用还是需要整体调整 // 先从整个dashboard页面出发,找出出现问题的原因,实在不行再尝试手动方案2 // 使用方案二 SDNSiteDashboardDeviceOverViewSkeletonView *deviceSkeletonView = [[SDNSiteDashboardDeviceOverViewSkeletonView alloc] init]; // 添加骨架 [_deviceOverView addSubview:deviceSkeletonView]; // 绑定约束 [deviceSkeletonView mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(_deviceOverView); }]; // 启动骨架动画 [self.deviceOverView layoutIfNeeded]; deviceSkeletonView.tabAnimated = [[TABViewAnimated alloc] init]; // 添加骨架波浪动画效果 deviceSkeletonView.tabAnimated.superAnimationType = TABViewSuperAnimationTypeShimmer; // networkView调整 _networkActivityView.tabAnimated.superAnimationType = TABViewSuperAnimationTypeShimmer; _networkActivityView.tabAnimated.adjustBlock = ^(TABComponentManager * _Nonnull manager) { // 整体移除不需要和显示不清晰的标签 manager.animations(21, 3).remove(); // 底部两个重叠的骨架块 // 对顶部0-15部分的修改:标签栏部分与坐标 manager.animation(0).reducedWidth(-20).reducedHeight(-8).radius(4); // 与安卓对齐 manager.animation(1).remove(); manager.animation(3).remove(); manager.animation(4).remove(); manager.animation(6).remove(); manager.animation(7).remove(); manager.animation(10).remove(); manager.animation(11).remove(); manager.animation(13).remove(); manager.animation(14).remove(); // // 对2、5、8、9、12、15作为目前的主题进行修改 // 左侧标识修改 manager.animation(2).reducedWidth(-60).reducedHeight(-5).radius(4); manager.animation(9).reducedWidth(-60).reducedHeight(-5).radius(4); // 右侧标识修改 manager.animation(5).x_offset(-60).reducedWidth(-40).reducedHeight(-5).radius(4); manager.animation(8).x_offset(-20).reducedWidth(-40).reducedHeight(-5).radius(4); manager.animation(12).x_offset(-60).reducedWidth(-40).reducedHeight(-5).radius(4); manager.animation(15).x_offset(-20).reducedWidth(-40).reducedHeight(-5).radius(4); // 对底部16-24的修改 manager.animation(20).remove(); manager.animations(17, 2).remove(); manager.animation(16).radius(4); manager.animation(19).remove(); // manager.animation(19).x_offset(100).reducedWidth(40).reducedHeight(25).radius(4); }; // wifiheadView调整 _WifiActivityHeadView.tabAnimated.superAnimationType = TABViewSuperAnimationTypeShimmer; // wifiView调整:调整wifiView的骨架屏以遮蔽“WiFi Aciivity”标签尝试-失败 _wifiActivityChartView.tabAnimated.superAnimationType = TABViewSuperAnimationTypeShimmer; _wifiActivityChartView.tabAnimated.adjustBlock = ^(TABComponentManager * _Nonnull manager) { CGRect screenBounds = [UIScreen mainScreen].bounds; CGFloat screenWidth = CGRectGetWidth(screenBounds); // 移除不需要与显示不清晰的骨架组件 // manager.animation(1).remove(); manager.animation(2).remove(); manager.animation(3).remove(); // 3和4是底边骨架识别来源,但识别位置过远 manager.animation(4).remove(); // 调整2 iconView的位置 manager.animation(1).radius(4); // 创建标识6和7:对应Upload manager.create(6).leftEqualTo_offset(1, screenWidth/2 + 70).y(6).width(50).height(16).radius(4); manager.create(7).leftEqualTo_offset(1, screenWidth/2 + 40).y(6).width(20).height(16).radius(4); // 创建标识8和9:对应Download manager.create(8).leftEqualTo_offset(1, screenWidth/2 - 30).y(6).width(50).height(16).radius(4); manager.create(9).leftEqualTo_offset(1, screenWidth/2 - 60).y(6).width(20).height(16).radius(4); }; // ApView调整:创建ApView的骨架 _apDensityContainerView.tabAnimated.superAnimationType = TABViewSuperAnimationTypeShimmer; _apDensityContainerView.tabAnimated.adjustBlock = ^(TABComponentManager * _Nonnull manager) { CGRect screenBounds = [UIScreen mainScreen].bounds; CGFloat screenWidth = CGRectGetWidth(screenBounds); // 标识0:titleBlockView manager.create(0) .width(120) .height(20) .x(16) .y(16) .radius(4); // // 标识1:indicatorBlockView // manager.create(1) // .width(24) // .height(20) // .x(screenWidth - 16 - 32 - 24) // 右边缘减去边距和宽度 // .topEqualTo_offset(0, 0) // 与 titleBlockView 顶部对齐 // .radius(4); // 标识2:densityBlockView manager.create(2) .width(screenWidth - 32 - 32 - 8) .height(60) // 可根据实际内容高度调整 .x(16) .topEqualTo_offset(0, 36) // titleBlockView 底部 + 间距16 .radius(4); }; // 启动骨架动画(逐个) // [self.deviceOverView tab_startAnimation]; [self.networkActivityView tab_startAnimation]; [self.WifiActivityHeadView tab_startAnimation]; [self.wifiActivityChartView tab_startAnimation]; [self.apDensityContainerView tab_startAnimation]; // 启动替代骨架 [deviceSkeletonView tab_startAnimation]; // // 延迟 10 秒后结束骨架动画并加载 tableView // dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ //// [self.deviceOverView tab_endAnimation]; // [self.networkActivityView tab_endAnimation]; // [self.WifiActivityHeadView tab_endAnimation]; // [self.wifiActivityChartView tab_endAnimation]; // [self.apDensityContainerView tab_endAnimation]; // // // // 关闭并移除替代骨架 // [deviceSkeletonView tab_endAnimation]; // [deviceSkeletonView removeFromSuperview]; // }); // 第 0 秒:关闭并移除骨架容器 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [deviceSkeletonView tab_endAnimation]; [deviceSkeletonView removeFromSuperview]; }); // 第 2 秒:networkActivityView dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self.networkActivityView tab_endAnimation]; }); // 第 4 秒:WifiActivityHeadView dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(6 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self.WifiActivityHeadView tab_endAnimation]; }); // 第 6 秒:wifiActivityChartView dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(6 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self.wifiActivityChartView tab_endAnimation]; }); // 第 8 秒:apDensityContainerView dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(8 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self.apDensityContainerView tab_endAnimation]; }); } 参考的TABAnimated用法: // 自动生成+手动调整 self.networkActivityView.tabAnimated = [[TABViewAnimated alloc] init]; // networkView调整 _networkActivityView.tabAnimated.superAnimationType = TABViewSuperAnimationTypeShimmer; _networkActivityView.tabAnimated.adjustBlock = ^(TABComponentManager * _Nonnull manager) { // 整体移除不需要和显示不清晰的标签 manager.animations(21, 3).remove(); // 底部两个重叠的骨架块 // 对顶部0-15部分的修改:标签栏部分与坐标 manager.animation(0).reducedWidth(-20).reducedHeight(-8).radius(4); // 与安卓对齐 manager.animation(1).remove(); manager.animation(3).remove(); manager.animation(4).remove(); manager.animation(6).remove(); manager.animation(7).remove(); manager.animation(10).remove(); manager.animation(11).remove(); manager.animation(13).remove(); manager.animation(14).remove(); // // 对2、5、8、9、12、15作为目前的主题进行修改 // 左侧标识修改 manager.animation(2).reducedWidth(-60).reducedHeight(-5).radius(4); manager.animation(9).reducedWidth(-60).reducedHeight(-5).radius(4); // 右侧标识修改 manager.animation(5).x_offset(-60).reducedWidth(-40).reducedHeight(-5).radius(4); manager.animation(8).x_offset(-20).reducedWidth(-40).reducedHeight(-5).radius(4); manager.animation(12).x_offset(-60).reducedWidth(-40).reducedHeight(-5).radius(4); manager.animation(15).x_offset(-20).reducedWidth(-40).reducedHeight(-5).radius(4); // 对底部16-24的修改 manager.animation(20).remove(); manager.animations(17, 2).remove(); manager.animation(16).radius(4); manager.animation(19).remove(); // manager.animation(19).x_offset(100).reducedWidth(40).reducedHeight(25).radius(4); }; // 启动骨架屏 [self.networkActivityView tab_startAnimation]; // 关闭骨架屏 [self.networkActivityView tab_endAnimation]; 使用替代骨架屏视图,需要自己传递一个遮挡原有视图的骨架屏 替代骨架屏方案的TABAnimated库骨架屏实现 // 调整骨架屏view // deviceView调整:只要启动回调,整个页面逻辑都会被破坏,TABA库的应用还是需要整体调整 // 先从整个dashboard页面出发,找出出现问题的原因,实在不行再尝试手动方案2 // 使用方案二 SDNSiteDashboardDeviceOverViewSkeletonView *deviceSkeletonView = [[SDNSiteDashboardDeviceOverViewSkeletonView alloc] init]; // 添加骨架 [_deviceOverView addSubview:deviceSkeletonView]; // 绑定约束 [deviceSkeletonView mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(_deviceOverView); }]; // 启动骨架动画 [self.deviceOverView layoutIfNeeded]; deviceSkeletonView.tabAnimated = [[TABViewAnimated alloc] init]; // 添加骨架波浪动画效果 deviceSkeletonView.tabAnimated.superAnimationType = TABViewSuperAnimationTypeShimmer; // 启动替代骨架 [deviceSkeletonView tab_startAnimation]; // 关闭替代骨架屏[deviceSkeletonView tab_endAnimation]; [deviceSkeletonView removeFromSuperview]; UITableView:TableView的TABAnimated库骨架屏实现 // 在慢加载中初始化 - (UITableView *)tableView { if (!_tableView) { _tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, kScreenHeight) style:UITableViewStyleGrouped]; _tableView.dataSource = self; _tableView.delegate = self; _tableView.estimatedRowHeight = 0; _tableView.estimatedSectionFooterHeight = 0; _tableView.estimatedSectionHeaderHeight = 0; _tableView.separatorStyle = UITableViewCellSeparatorStyleNone; _tableView.backgroundColor = [UIColor tab_normalDynamicBackgroundColor]; // 设置tabAnimated相关属性 // 部分section有动画 _tableView.tabAnimated = [TABTableAnimated animatedWithCellClass:[TestTableViewCell class] cellHeight:100 animatedCount:1 toSection:1]; [_tableView.tabAnimated addHeaderViewClass:[LineTableViewHeaderFooterView class] viewHeight:60 toSection:1]; _tableView.tabAnimated.adjustWithClassBlock = ^(TABComponentManager *manager, __unsafe_unretained Class targetClass) { if (targetClass == TestTableViewCell.class) { manager.animation(1).down(3).height(12); manager.animation(2).height(12).width(110); manager.animation(3).down(-5).height(12); }else if (targetClass == LineTableViewHeaderFooterView.class) { manager.animation(2).right(3).height(14).down(16).reducedWidth(30).radius(2); } }; } return _tableView; } // 在UI初始化中启动骨架屏 /** initize view 视图初始化 */ - (void)initUI { [self.view addSubview:self.tableView]; [self.tableView tab_startAnimation]; // 开启动画 } // 获取到数据后进行对应骨架屏取消 /** 获取到数据后 */ - (void)afterGetData { [dataArray removeAllObjects]; // 模拟数据 for (int i = 0; i < 10; i ++) { Game *game = [[Game alloc]init]; game.gameId = [NSString stringWithFormat:@"%d",i]; game.title = [NSString stringWithFormat:@"这里是测试数据%d",i+1]; game.cover = @"test.jpg"; [dataArray addObject:game]; } // 停止动画,并刷新数据 [self.tableView tab_endAnimationWithIndex:1]; } UICollectionView的TABAnimated库骨架屏实现 // 1、初始化_collectionView.tabAnimated = [TABCollectionAnimated animatedWithCellClass:[NewsCollectionViewCell class] cellSize:[NewsCollectionViewCell cellSize]]; // 2、控制骨架屏开关 // 开启动画 [self.collectionView tab_startAnimation]; // 关闭动画 [self.collectionView tab_endAnimation]; // 3、预处理回调+链式语法用于修改骨架元素的属性 // 用变量名 _tableView.tabAnimated.adjustBlock = ^(TABComponentManager * _Nonnull manager) { manager.animationN(@"titleImageView").down(3).radius(12); manager.animationN(@"nameLabel").height(12).width(110); manager.animationN(@"timeButton").down(-5).height(12); }; // 用index修改 _tableView.tabAnimated.adjustBlock = ^(TABComponentManager * _Nonnull manager) { manager.animation(1).down(3).radius(12); manager.animation(2).height(12).width(110); manager.animation(3).down(-5).height(12); }; // 使用swift tableView.tabAnimated?.adjustBlock = { manager in manager.animation()?(1)?.down()(3)?.radius()(12) manager.animation()?(2)?.height()(12)?.width()(110) manager.animation()?(3)?.down()(-5)?.height()(12) }
11-01
// // XSJLiveAudienceInnerView.m // XSJ // // Created by Zzz on 2020/5/28. // Copyright © 2020 apple. All rights reserved. // #import "XSJLiveLinkMicInnerView.h" #import "NTESLiveManager.h" #import "NTESMicConnector.h" #import "UIView+XSJExtension.h" #import "NTESGLView.h" #import "NTESMediaG2Instance.h" #import "XSJ_LiveMicUserModel.h" #import "XSJ_AnchorLinkMicView.h" @interface XSJLiveLinkMicInnerView()<NTESLiveBypassViewDelegate> @property (nonatomic, copy) NSString *roomId; @property (nonatomic, assign) BOOL isAnchor; @property (nonatomic, strong) XSJ_AnchorLinkMicView *glViewContainer; //接收YUV数据的视图 @end @implementation XSJLiveLinkMicInnerView static CGFloat widthSpace = 6; static CGFloat heightSpace = 0; //#define buttonWidth XSJScreenWidth / 2 #define buttonWidth (ScreenWidth/2.0)//(XSJScreenWidth - widthSpace) / 2 //#define KLinkMicInnerViewOriginalHeight XSJScreenWidth / 2 + (XSJIS_PhoneXAll ? 90 : 50) #define startX buttonWidth//XSJScreenWidth / 2 - (instancetype)initWithChatroom:(NSString *)chatroomId frame:(CGRect)frame isAnchor:(BOOL)isAnchor { self = [super initWithFrame:frame]; if (self) { _roomId = chatroomId; _isAnchor = isAnchor; [self setupUI]; } return self; } - (void)layoutSubviews { [super layoutSubviews]; self.glViewContainer.size = self.size; NSLog(@"连麦视图-----> %zd",self.bypassViews.count); NSMutableArray *connectorsOnMic = [NTESLiveManager sharedInstance].connectorsOnMic; NSInteger total = connectorsOnMic.count; [self.bypassViews enumerateObjectsUsingBlock:^(NTESLiveBypassView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { NSInteger index = idx + 1; NSInteger col = index % 2; NSInteger row = index / 2; CGFloat itemW = buttonWidth; CGFloat itemH = KLinkMicInnerViewOriginalHeight; if (total > 1) { itemH = itemW; } obj.frame = CGRectMake(col*itemW, row*itemH+KLinkMicInnerViewStartY, itemW, itemH); }]; if (!self.isAnchor) { if (total > 1) { //四宫格 //主播的view 在第一格 self.glViewContainer.frame = CGRectMake(0, KLinkMicInnerViewStartY, buttonWidth, buttonWidth); }else{ //左右排布 if (total == 1) { self.glViewContainer.frame = CGRectMake(0, KLinkMicInnerViewStartY, buttonWidth, KLinkMicInnerViewOriginalHeight); } } } else{ //主播自己的本地view //左右排布 self.glViewContainer.hidden = NO; if (total < 1) { //自己1个人 self.glViewContainer.hidden = YES; self.localDisplayView.frame = CGRectMake(0, 0, XSJScreenWidth, XSJScreenHeight); }else if (total == 1) { self.localDisplayView.frame = CGRectMake(0, KLinkMicInnerViewStartY, buttonWidth, KLinkMicInnerViewOriginalHeight); }else { self.localDisplayView.frame = CGRectMake(0, KLinkMicInnerViewStartY, buttonWidth, buttonWidth); } self.glViewContainer.frame = self.localDisplayView.frame; } // 计算视图的宽高 CGFloat viewHeight = 0; if (self.bypassViews.count > 0) { UIView *view = self.bypassViews.lastObject; viewHeight = view.bottom - KLinkMicInnerViewStartY; } else { viewHeight = XSJScreenHeight; } if (self.delegate && [self.delegate respondsToSelector:@selector(refreshBypassUIEndWithLinkCount:viewSize:)]) { [self.delegate refreshBypassUIEndWithLinkCount:_bypassViews.count viewSize:CGSizeMake(kScreenWidth, viewHeight)]; } } - (void)addRemoteViewForConnector:(NTESMicConnector *)connector{ [self addRemoteViewForUid:connector.uid rtcUid:connector.rtcUid]; } - (void)addRemoteViewForUid:(NSString *)uid rtcUid:(UInt64)rtcUid { if ([NTESLiveManager sharedInstance].role == NTESLiveRoleAnchor){ NTESLiveBypassView *bypassView = [self bypassViewWithUid:uid]; if (!bypassView) { return; } UIView *container = bypassView.remoteViewContainer ? bypassView.remoteViewContainer : bypassView; [[NTESMediaG2Instance sharedInstance] setUpRemoteView:container userId:rtcUid]; } else { NIMChatroom *roomInfo = [[NTESLiveManager sharedInstance] roomInfo:self.roomId]; if ([uid isEqualToString:roomInfo.creator]) { NSMutableArray *connectorsOnMic = [NTESLiveManager sharedInstance].connectorsOnMic; [[NTESMediaG2Instance sharedInstance] setUpRemoteView:self.glViewContainer.glView userId:rtcUid]; } else { NTESLiveBypassView *bypassView = [self bypassViewWithUid:uid]; if (!bypassView) { return; } UIView *container = bypassView.remoteViewContainer ? bypassView.remoteViewContainer : bypassView; [[NTESMediaG2Instance sharedInstance] setUpRemoteView:container userId:rtcUid]; } } } #pragma mark - 查询连麦视图 - (NTESLiveBypassView *)bypassViewWithUid:(NSString *)uid { __block NTESLiveBypassView *ret = nil; [_bypassViews enumerateObjectsUsingBlock:^(NTESLiveBypassView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { if ([obj.uid isEqualToString:uid]) { ret = obj; *stop = YES; } }]; return ret; } - (NTESLiveBypassView *)bypassViewWithLineUid:(NSInteger)lineUid { __block NTESLiveBypassView *ret = nil; [_bypassViews enumerateObjectsUsingBlock:^(NTESLiveBypassView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { if (lineUid == obj.lineUid) { ret = obj; *stop = YES; } }]; return ret; } #pragma mark - 连麦视图 // 添加连麦视图 - (NTESLiveBypassView *)addBypassViewWithConnector:(NTESMicConnector *)connector { DDLog(@"YAT addBypassViewWithConnector %@",connector.uid); __block BOOL isAdd = YES; __block NTESLiveBypassView *ret = nil; [_bypassViews enumerateObjectsUsingBlock:^(NTESLiveBypassView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { if ([obj.uid isEqualToString:connector.uid]) { isAdd = NO; ret = obj; *stop = YES; } }]; //创建 并 addSubView if (isAdd) { DDLog(@"YAT really addBypassViewWithConnector %@",connector.uid); NTESLiveBypassView *bypassView = [[NTESLiveBypassView alloc] initWithFrame:CGRectZero]; bypassView.isAnchor = _isAnchor; bypassView.uid = connector.uid; bypassView.lineUid = connector.rtcUid; [self didUpdatePassView:bypassView connector:connector]; [self.bypassViews addObject:bypassView]; [self addSubview:bypassView]; ret = bypassView; }else { // NTESLiveBypassView *byPassView = [self bypassViewWithUid:connector.uid]; // if (byPassView) { // [self didUpdatePassView:byPassView connector:connector]; // } } return ret; } - (void)didUpdatePassView:(NTESLiveBypassView *)bypassView connector:(NTESMicConnector *)connector { if (!connector) { return; } bypassView.uid = connector.uid; bypassView.delegate = self; bypassView.connector = connector; if ([connector.uid isEqualToString:[[NIMSDK sharedSDK].loginManager currentAccount]]) { bypassView.localVideoDisplayView = self.localDisplayView; } [bypassView sizeToFit]; if (!_bypassViews) { _bypassViews = [NSMutableArray array]; } if (connector.type == NIMNetCallMediaTypeAudio) { if (connector.state == NTESLiveMicStateConnected) { [bypassView refresh:connector status:NTESLiveBypassViewStatusStreamingAudio]; } else if (connector.state == NTESLiveMicStateConnecting) { [bypassView refresh:connector status:NTESLiveBypassViewStatusLoading]; } else { [bypassView refresh:connector status:NTESLiveBypassViewStatusPlayingAndBypassingAudio]; } } else { if (connector.state == NTESLiveMicStateConnected) { [bypassView refresh:connector status:NTESLiveBypassViewStatusStreamingVideo]; } else if (connector.state == NTESLiveMicStateConnecting) { [bypassView refresh:connector status:NTESLiveBypassViewStatusLoading]; } else { [bypassView refresh:connector status:NTESLiveBypassViewStatusPlaying]; } } } - (void)updateUserInfos:(NSArray <XSJ_LiveMicUserModel*> *)userInfos { for (XSJ_LiveMicUserModel *userInfo in userInfos) { NTESLiveBypassView *bypassView = [self bypassViewWithLineUid:userInfo.lineUid]; if (bypassView) { [bypassView refreshUserModel:userInfo]; } } } - (void)switchToBypassingUI:(NTESMicConnector *)connector { [self refreshBypassUIWithConnector:connector]; } - (void)switchToBypassLoadingUI:(NTESMicConnector *)connector { [self refreshBypassUI]; self.localDisplayView.frame = CGRectMake(0, KLinkMicInnerViewStartY, buttonWidth, KLinkMicInnerViewOriginalHeight); NTESLiveBypassView *bypassView = [self bypassViewWithUid:connector.uid]; [bypassView refresh:connector status:NTESLiveBypassViewStatusLoading]; [self setNeedsLayout]; } - (void)switchToBypassStreamingUI:(NTESMicConnector *)connector { NTESLiveBypassViewStatus status = connector.type == NIMNetCallMediaTypeAudio? NTESLiveBypassViewStatusStreamingAudio: NTESLiveBypassViewStatusStreamingVideo; [self refreshBypassUI]; NTESLiveBypassView *bypassView = [self bypassViewWithUid:connector.uid]; [bypassView refresh:connector status:status]; self.glViewContainer.hidden = YES; } - (void)refreshBypassUIWithConnector:(NTESMicConnector *)connector { DDLog(@"YAT refreshBypassUIWithConnector uid %@",connector.uid); [self refreshBypassUI]; NTESLiveBypassView *bypassView = [self bypassViewWithUid:connector.uid]; NTESLiveBypassViewStatus status; status = connector.type == NIMNetCallMediaTypeAudio ? NTESLiveBypassViewStatusStreamingAudio : NTESLiveBypassViewStatusStreamingVideo; [bypassView refresh:connector status:status]; } - (void)refreshMicrophoneUIWithConnector:(NTESMicConnector *)connector Mute:(BOOL)Mute { DDLog(@"YAT refreshBypassUIWithConnector uid %@",connector.uid); NTESLiveBypassView *bypassView = [self bypassViewWithUid:connector.uid]; NTESLiveBypassViewStatus status = connector.type == NIMNetCallMediaTypeAudio ? NTESLiveBypassViewStatusStreamingAudio : NTESLiveBypassViewStatusStreamingVideo; [bypassView refresh:connector status:status]; } //刷新视频 - (void)refreshMicVideoUIWithConnector:(NTESMicConnector *)connector Mute:(BOOL)Mute { DDLog(@"YAT refreshBypassUIWithConnector uid %@",connector.uid); NTESLiveBypassView *bypassView = [self bypassViewWithUid:connector.uid]; [bypassView refresh:connector status:NTESLiveBypassViewStatusStreamingVideo]; } - (void)refreshBypassUI { if ([_delegate isPlayerPlaying] && ![_delegate isAudioMode]) { [_bypassViews enumerateObjectsUsingBlock:^(NTESLiveBypassView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { [obj removeFromSuperview]; }]; [_bypassViews removeAllObjects]; } else { //刚开始连麦走这里 __block NSMutableIndexSet *delIndex = [NSMutableIndexSet indexSet]; NSMutableArray *connectorsOnMic = [NTESLiveManager sharedInstance].connectorsOnMic; __weak typeof(self) weakSelf = self; [_bypassViews enumerateObjectsUsingBlock:^(NTESLiveBypassView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { __block BOOL isExist = NO; [connectorsOnMic enumerateObjectsUsingBlock:^(NTESMicConnector *_Nonnull d_obj, NSUInteger d_idx, BOOL * _Nonnull d_stop) { if ([obj.uid isEqualToString:d_obj.uid]) { isExist = YES; *d_stop = YES; } }]; if (!isExist) { DDLog(@"--zgn-- 移除bypass view:[%@]", obj.uid); [delIndex addIndex:idx]; [obj removeFromSuperview]; } }]; if (delIndex.count != 0) { [_bypassViews removeObjectsAtIndexes:delIndex]; } [connectorsOnMic enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { [weakSelf addBypassViewWithConnector:obj]; }]; } } - (void)didClearAllBypassUI { [_bypassViews enumerateObjectsUsingBlock:^(NTESLiveBypassView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { [obj removeFromSuperview]; }]; [_bypassViews removeAllObjects]; } - (void)switchToPlayingUI { if ([NTESLiveManager sharedInstance].connectorsOnMic.count < 1) { self.localDisplayView.frame = CGRectMake(0, 0, XSJScreenWidth, XSJScreenHeight); } else { self.localDisplayView.frame = CGRectMake(0, KLinkMicInnerViewStartY, buttonWidth, KLinkMicInnerViewOriginalHeight); } NIMChatroom *room = [[NTESLiveManager sharedInstance] roomInfo:self.roomId]; if (!room) { DDLog(@"chat room has not entered, ignore show playing UI"); return; } [self refreshBypassUI]; } //清空所有byPassView - (void)clearByPassView { @synchronized (self.bypassViews) { [self.bypassViews makeObjectsPerformSelector:@selector(removeFromSuperview)]; [self.bypassViews removeAllObjects]; } } #pragma mark - 连麦事件 - (void)endLinkMicAction { if ([self.delegate respondsToSelector:@selector(stopLinkMicAction)]) { [self.delegate stopLinkMicAction]; } } #pragma mark - NTESLiveBypassViewDelegate - (void)didConfirmExitBypassWithUid:(NSString *)uid { if ([self.delegate respondsToSelector:@selector(onCloseBypassingWithUid:)]) { [self.delegate onCloseBypassingWithUid:uid]; } } - (void)refreshMicrophoneUIWithUid:(NSString *)uid Mute:(BOOL)Mute { if ([self.delegate respondsToSelector:@selector(onCloseBypassingWithMicrophoneUid:Mute:)]) { [self.delegate onCloseBypassingWithMicrophoneUid:uid Mute:Mute]; } } /// 显示用户信息 - (void)liveBypassView:(NTESLiveBypassView *)view showUserInfo:(NSString *)userId { if ([self.delegate respondsToSelector:@selector(showUserInfoAlertWithUid:)]) { [self.delegate showUserInfoAlertWithUid:userId]; } } - (void)setupUI { self.localDisplayView.frame = CGRectMake(0, 0, XSJScreenWidth, XSJScreenHeight); [self addSubview:self.glViewContainer]; } - (XSJ_AnchorLinkMicView *)glViewContainer { if (!_glViewContainer) { _glViewContainer = [[XSJ_AnchorLinkMicView alloc] initWithFrame:self.bounds]; WEAKSELF _glViewContainer.showUserInfoBlock = ^{ if ([weakSelf.delegate respondsToSelector:@selector(showUserInfoAlertWithOwner)]) { [weakSelf.delegate showUserInfoAlertWithOwner]; } }; } return _glViewContainer; } - (UIView *)localDisplayView{ if (!_localDisplayView) { _localDisplayView = [[UIView alloc ] initWithFrame:CGRectMake(0, 0, XSJScreenWidth, XSJScreenHeight)]; } return _localDisplayView; } @end 优化上面的代码
07-18
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值