| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322 |
- //
- // LNVideoFeedDetailViewController.swift
- // Gami
- //
- // Created by OneeChan on 2026/3/3.
- //
- import Foundation
- import UIKit
- import SnapKit
- extension UIView {
- func pushToVideoFeedDetail(id: String) {
- let vc = LNVideoFeedDetailViewController()
- vc.loadDetail(id: id)
- navigationController?.pushViewController(vc, animated: true)
- }
- }
- class LNVideoFeedDetailViewController: LNViewController {
- private let avatar = UIImageView()
- private let nameLabel = UILabel()
-
- private let contentLabel = UILabel()
- private let timeLabel = UILabel()
-
- private let seekBar = UISlider()
-
- private let inputField = UITextField()
-
- private let likeView = LNFeedLikeView()
- private let commentView = LNFeedCommentView()
-
- private let videoPlayer = LNVideoPlayerView()
-
- private var curDetail: LNFeedDetailVO?
-
- override func viewDidLoad() {
- super.viewDidLoad()
-
- setupViews()
- LNEventDeliver.addObserver(self)
- }
-
- func loadDetail(id: String) {
- LNFeedManager.shared.getFeedDetail(id: id) { [weak self] detail in
- guard let self else { return }
- guard let detail, let video = detail.medias.first(where: { $0.type == .video }) else {
- navigationController?.popViewController(animated: true)
- return
- }
-
- curDetail = detail
-
- avatar.sd_setImage(with: URL(string: detail.avatar))
- nameLabel.text = detail.nickname
- likeView.update(id: detail.id, liked: detail.liked, count: detail.likeCount)
- commentView.update(id: detail.id, count: detail.commentCount)
- contentLabel.text = detail.textContent
- timeLabel.text = TimeInterval(detail.createdAt / 1_000).tencentIMTimeDesc
- videoPlayer.loadVideo(video.url, coverUrl: video.videoCover)
- }
- }
- }
- extension LNVideoFeedDetailViewController {
- private func toComment() {
- guard let curDetail else { return }
- let panel = LNCommonInputPanel()
- panel.maxInput = LNFeedManager.feedCommentMaxInput
- panel.handler = { comment in
- LNFeedManager.shared.sendFeedComment(id: curDetail.id, content: comment)
- { success in
- guard success else { return }
- curDetail.commentCount += 1
- LNFeedManager.shared.notifyFeedCommentChanged(id: curDetail.id, count: curDetail.commentCount)
- }
- }
- panel.popup()
- }
- }
- extension LNVideoFeedDetailViewController {
- @objc private func sliderTouchBegan() {
- videoPlayer.pause()
- }
-
- @objc private func sliderTouchEnded() {
- videoPlayer.seekTo(seekBar.value) { [weak self] finished in
- guard let self, finished else { return }
- videoPlayer.start()
- }
- }
- }
- extension LNVideoFeedDetailViewController: LNVideoPlayerViewDelegate {
- func onVideoDidLoad(view: LNVideoPlayerView) {
- seekBar.isUserInteractionEnabled = true
- seekBar.minimumValue = 0
- seekBar.maximumValue = Float(view.duration)
- seekBar.value = 0
- view.start()
- }
-
- func onVideoProgressChanged(view: LNVideoPlayerView, cur: Float64, total: Float64) {
- seekBar.setValue(Float(cur), animated: true)
- }
- }
- extension LNVideoFeedDetailViewController {
- private func setupViews() {
- view.backgroundColor = .black
-
- setupNavBar()
-
- let bottomMenu = buildMenuView()
- view.addSubview(bottomMenu)
- bottomMenu.snp.makeConstraints { make in
- make.horizontalEdges.equalToSuperview()
- make.bottom.equalTo(view.safeAreaLayoutGuide.snp.bottom)
- }
-
- videoPlayer.showCover = false
- videoPlayer.loop = true
- videoPlayer.delegate = self
- videoPlayer.backgroundColor = .clear
- videoPlayer.playButtonSize = 56
- view.addSubview(videoPlayer)
- videoPlayer.snp.makeConstraints { make in
- make.horizontalEdges.equalToSuperview()
- make.top.equalTo(fakeNaviBgView)
- make.bottom.equalTo(bottomMenu.snp.top)
- }
-
- seekBar.isUserInteractionEnabled = false
- seekBar.addTarget(self, action: #selector(sliderTouchBegan), for: .touchDown)
- seekBar.addTarget(self, action: #selector(sliderTouchEnded), for: .touchUpInside)
- seekBar.addTarget(self, action: #selector(sliderTouchEnded), for: .touchUpOutside)
- seekBar.minimumTrackTintColor = .text_1
- seekBar.setThumbImage(UIImage.image(for: .white, size: 6, cornerRadius: 3), for: .normal)
- view.addSubview(seekBar)
- seekBar.snp.makeConstraints { make in
- make.horizontalEdges.equalToSuperview()
- make.bottom.equalTo(bottomMenu.snp.top)
- make.height.equalTo(6)
- }
-
- let content = buildContent()
- view.addSubview(content)
- content.snp.makeConstraints { make in
- make.horizontalEdges.equalToSuperview().inset(16)
- make.bottom.equalTo(seekBar.snp.top).offset(-16)
- }
- }
-
- private func setupNavBar() {
- navigationBarColor = .clear
- backButton.tintColor = .text_1
-
- let menu = buildButtonMenu()
- setRightButton(menu)
-
- let container = UIView()
- avatar.layer.cornerRadius = 16
- avatar.clipsToBounds = true
- container.addSubview(avatar)
- avatar.snp.makeConstraints { make in
- make.leading.equalToSuperview()
- make.centerY.equalToSuperview()
- make.width.height.equalTo(32)
- }
-
- nameLabel.font = .heading_h3
- nameLabel.textColor = .text_1
- nameLabel.setContentHuggingPriority(.defaultLow, for: .horizontal)
- nameLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
- container.addSubview(nameLabel)
- nameLabel.snp.makeConstraints { make in
- make.leading.equalTo(avatar.snp.trailing).offset(12)
- make.centerY.equalToSuperview()
- make.trailing.lessThanOrEqualToSuperview().offset(-37)
- }
-
- setTitleView(container)
- container.snp.makeConstraints { make in
- make.width.equalTo(view.bounds.width).priority(.medium)
- make.height.equalTo(44)
- }
- }
-
- private func buildButtonMenu() -> UIView {
- let moreButton = UIButton()
- moreButton.layer.cornerRadius = 16
- moreButton.setImage(.icMore.withRenderingMode(.alwaysTemplate), for: .normal)
- moreButton.tintColor = .text_2
- moreButton.snp.makeConstraints { make in
- make.width.height.equalTo(32)
- }
- moreButton.addAction(UIAction(handler: { [weak self] _ in
- guard let self else { return }
- guard let curDetail else { return }
- LNBottomSheetMenu.showFeedMenu(detail: curDetail, view: view)
- }), for: .touchUpInside)
-
- return moreButton
- }
-
- private func buildMenuView() -> UIView {
- let container = UIView()
- container.snp.makeConstraints { make in
- make.height.equalTo(58)
- }
-
- let comment = buildComment()
- container.addSubview(comment)
- comment.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.trailing.equalToSuperview().offset(-16)
- }
-
- let like = buildLike()
- container.addSubview(like)
- like.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.trailing.equalTo(comment.snp.leading).offset(-20)
- }
-
- let input = buildInput()
- container.addSubview(input)
- input.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.leading.equalToSuperview().offset(16)
- make.width.equalTo(190)
- }
-
- return container
- }
-
- private func buildComment() -> UIView {
- commentView.uiColor = .text_1
- commentView.onTap { [weak self] in
- guard let self else { return }
- guard let curDetail else { return }
- if curDetail.commentCount == 0 {
- toComment()
- } else {
- let panel = LNFeedCommentListPanel()
- panel.load(curDetail.id)
- panel.popup()
- }
- }
-
- return commentView
- }
-
- private func buildLike() -> UIView {
- likeView.uiColor = .text_1
-
- return likeView
- }
-
- private func buildInput() -> UIView {
- let container = UIView()
- container.backgroundColor = .text_5
- container.layer.cornerRadius = 19
- container.onTap { [weak self] in
- guard let self else { return }
- toComment()
- }
- container.snp.makeConstraints { make in
- make.height.equalTo(38)
- }
-
- let editIc = UIImageView()
- editIc.image = .icImChatMenuRemark.withTintColor(.text_2)
- container.addSubview(editIc)
- editIc.snp.makeConstraints { make in
- make.leading.equalToSuperview().offset(10)
- make.centerY.equalToSuperview()
- make.width.height.equalTo(24)
- }
-
- let tipsLabel = UILabel()
- tipsLabel.font = .body_m
- tipsLabel.textColor = .text_2
- tipsLabel.text = .init(key: "A00300")
- container.addSubview(tipsLabel)
- tipsLabel.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.leading.equalTo(editIc.snp.trailing).offset(4)
- make.trailing.equalToSuperview().offset(-10)
- }
-
- return container
- }
-
- private func buildContent() -> UIView {
- let container = UIView()
-
- contentLabel.font = .body_m
- contentLabel.textColor = .text_1
- contentLabel.numberOfLines = 0
- container.addSubview(contentLabel)
- contentLabel.snp.makeConstraints { make in
- make.horizontalEdges.equalToSuperview()
- make.top.equalToSuperview()
- }
-
- timeLabel.font = .body_xs
- timeLabel.textColor = .text_2
- container.addSubview(timeLabel)
- timeLabel.snp.makeConstraints { make in
- make.leading.equalToSuperview()
- make.top.equalTo(contentLabel.snp.bottom).offset(8)
- make.bottom.equalToSuperview()
- }
-
- return container
- }
- }
|