Skip to content

Commit

Permalink
feat(Comment): support multi-level comment preview, slightly refactor…
Browse files Browse the repository at this point in the history
… ReplyCell for reuse. (#107)

Co-authored-by: Yam <[email protected]>
  • Loading branch information
yam-liu and Yam authored Jun 10, 2024
1 parent ae536cc commit 09d30ee
Show file tree
Hide file tree
Showing 8 changed files with 280 additions and 20 deletions.
29 changes: 29 additions & 0 deletions BilibiliLive.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
0A41EE1C2A63102B0066444C /* dm.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A41EE1A2A63102B0066444C /* dm.pb.swift */; };
0A41EE1D2A63102B0066444C /* dmView.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A41EE1B2A63102B0066444C /* dmView.pb.swift */; };
27FECFCC2B0B98F400EC6A6D /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 27FECFCB2B0B98F400EC6A6D /* Localizable.xcstrings */; };
2806E51E2C1593A000164C10 /* LookinServer in Frameworks */ = {isa = PBXBuildFile; productRef = 2806E51D2C1593A000164C10 /* LookinServer */; };
2806E5202C15A59E00164C10 /* ReplyDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2806E51F2C15A59E00164C10 /* ReplyDetailViewController.swift */; };
2806E5232C16011B00164C10 /* ReplyCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2806E5212C16011B00164C10 /* ReplyCell.swift */; };
2806E5242C16011B00164C10 /* ReplyCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2806E5222C16011B00164C10 /* ReplyCell.xib */; };
2DBE4C4D2628818F00D20413 /* HistoryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DBE4C4C2628818F00D20413 /* HistoryViewController.swift */; };
490425F729AB54B200CDBC60 /* CategoryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 490425F629AB54B200CDBC60 /* CategoryViewController.swift */; };
49078E47291BEA2400F556BD /* PocketSVG in Frameworks */ = {isa = PBXBuildFile; productRef = 49078E46291BEA2400F556BD /* PocketSVG */; };
Expand Down Expand Up @@ -138,6 +142,9 @@
0A41EE1A2A63102B0066444C /* dm.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = dm.pb.swift; sourceTree = "<group>"; };
0A41EE1B2A63102B0066444C /* dmView.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = dmView.pb.swift; sourceTree = "<group>"; };
27FECFCB2B0B98F400EC6A6D /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = "<group>"; };
2806E51F2C15A59E00164C10 /* ReplyDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyDetailViewController.swift; sourceTree = "<group>"; };
2806E5212C16011B00164C10 /* ReplyCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ReplyCell.swift; path = BilibiliLive/Component/View/ReplyCell.swift; sourceTree = SOURCE_ROOT; };
2806E5222C16011B00164C10 /* ReplyCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = ReplyCell.xib; path = BilibiliLive/Component/View/ReplyCell.xib; sourceTree = SOURCE_ROOT; };
2DBE4C4C2628818F00D20413 /* HistoryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryViewController.swift; sourceTree = "<group>"; };
490425F629AB54B200CDBC60 /* CategoryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoryViewController.swift; sourceTree = "<group>"; };
490EC3E6290CC8F8001E00B6 /* RankingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RankingViewController.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -317,6 +324,7 @@
49508E0F2943420100D26812 /* CocoaLumberjack in Frameworks */,
499C75EC293058C9003160FB /* CocoaAsyncSocket in Frameworks */,
F927ED9F2610B5C300EAB8E3 /* Kingfisher in Frameworks */,
2806E51E2C1593A000164C10 /* LookinServer in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -364,6 +372,9 @@
children = (
F9562C91261A0D2200573B74 /* VideoPlayerViewController.swift */,
F9EDADD2262AA421007CB99F /* VideoDetailViewController.swift */,
2806E51F2C15A59E00164C10 /* ReplyDetailViewController.swift */,
2806E5212C16011B00164C10 /* ReplyCell.swift */,
2806E5222C16011B00164C10 /* ReplyCell.xib */,
49389D6128AFEA2900B9DAFD /* VideoDanmuProvider.swift */,
498DB1DE291BC24700F95607 /* BMaskProvider.swift */,
49FB8EBF291F4C520045D5DE /* VMaskProvider.swift */,
Expand Down Expand Up @@ -697,6 +708,7 @@
49508E0E2943420100D26812 /* CocoaLumberjack */,
49508E102943420100D26812 /* CocoaLumberjackSwift */,
0A41EE182A630FEA0066444C /* SwiftProtobuf */,
2806E51D2C1593A000164C10 /* LookinServer */,
);
productName = BilibiliLive;
productReference = F9B57350260F5F7400771ED5 /* BilibiliLive.app */;
Expand Down Expand Up @@ -740,6 +752,7 @@
499C76142931A7AE003160FB /* XCRemoteSwiftPackageReference "SwiftyXMLParser" */,
49508E0D2943420100D26812 /* XCRemoteSwiftPackageReference "CocoaLumberjack" */,
0A41EE172A630FEA0066444C /* XCRemoteSwiftPackageReference "swift-protobuf" */,
2806E51C2C1593A000164C10 /* XCRemoteSwiftPackageReference "LookinServer" */,
);
productRefGroup = F9B57351260F5F7400771ED5 /* Products */;
projectDirPath = "";
Expand All @@ -759,6 +772,7 @@
49F918722931E927001D3EC3 /* AvTransportScpd.xml in Resources */,
F9B5735E260F5F7600771ED5 /* LaunchScreen.storyboard in Resources */,
F9B5735B260F5F7600771ED5 /* Assets.xcassets in Resources */,
2806E5242C16011B00164C10 /* ReplyCell.xib in Resources */,
49F9186E2931E3C9001D3EC3 /* DLNAInfo.xml in Resources */,
27FECFCC2B0B98F400EC6A6D /* Localizable.xcstrings in Resources */,
F9B57359260F5F7400771ED5 /* Main.storyboard in Resources */,
Expand Down Expand Up @@ -801,6 +815,7 @@
497361082BF1A16600ED213F /* Keys.swift in Sources */,
49E5F85028AF73C500FAA3CE /* BilibiliVideoResourceLoaderDelegate.swift in Sources */,
498CF2A22B63AABE0009793E /* dictionary.c in Sources */,
2806E5202C15A59E00164C10 /* ReplyDetailViewController.swift in Sources */,
494741C82902C45D005D6885 /* Array+..swift in Sources */,
498CF29E2B63AABE0009793E /* bit_cost.c in Sources */,
498CF29B2B63AABE0009793E /* utf8_util.c in Sources */,
Expand Down Expand Up @@ -849,6 +864,7 @@
494741E7290391A7005D6885 /* BLButton.swift in Sources */,
F927ED992610AD8D00EAB8E3 /* LiveViewController.swift in Sources */,
490EC3E7290CC8F8001E00B6 /* RankingViewController.swift in Sources */,
2806E5232C16011B00164C10 /* ReplyCell.swift in Sources */,
F927ED732610395300EAB8E3 /* DanmakuCell.swift in Sources */,
498CF2A92B63AABE0009793E /* bit_reader.c in Sources */,
498CF2972B63AABE0009793E /* encode.c in Sources */,
Expand Down Expand Up @@ -1126,6 +1142,14 @@
minimumVersion = 1.0.0;
};
};
2806E51C2C1593A000164C10 /* XCRemoteSwiftPackageReference "LookinServer" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/QMUI/LookinServer/";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 1.2.8;
};
};
49078E45291BEA2400F556BD /* XCRemoteSwiftPackageReference "PocketSVG" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/pocketsvg/PocketSVG.git";
Expand Down Expand Up @@ -1222,6 +1246,11 @@
package = 0A41EE172A630FEA0066444C /* XCRemoteSwiftPackageReference "swift-protobuf" */;
productName = SwiftProtobuf;
};
2806E51D2C1593A000164C10 /* LookinServer */ = {
isa = XCSwiftPackageProductDependency;
package = 2806E51C2C1593A000164C10 /* XCRemoteSwiftPackageReference "LookinServer" */;
productName = LookinServer;
};
49078E46291BEA2400F556BD /* PocketSVG */ = {
isa = XCSwiftPackageProductDependency;
package = 49078E45291BEA2400F556BD /* XCRemoteSwiftPackageReference "PocketSVG" */;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"originHash" : "f89b743e598074e66556cd063dd1258c33483d783163efc65f648349cece8790",
"pins" : [
{
"identity" : "alamofire",
Expand Down Expand Up @@ -45,6 +46,15 @@
"version" : "6.3.1"
}
},
{
"identity" : "lookinserver",
"kind" : "remoteSourceControl",
"location" : "https://github.com/QMUI/LookinServer/",
"state" : {
"revision" : "e553d1b689d147817dc54ad5c28fcff71e860101",
"version" : "1.2.8"
}
},
{
"identity" : "marqueelabel",
"kind" : "remoteSourceControl",
Expand Down Expand Up @@ -118,5 +128,5 @@
}
}
],
"version" : 2
"version" : 3
}
13 changes: 7 additions & 6 deletions BilibiliLive/Base.lproj/Main.storyboard
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder.AppleTV.Storyboard" version="3.0" toolsVersion="21507" targetRuntime="AppleTV" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder.AppleTV.Storyboard" version="3.0" toolsVersion="32700.99.1234" targetRuntime="AppleTV" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="appleTV" appearance="dark"/>
<dependencies>
<deployment identifier="tvOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22684"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
Expand Down Expand Up @@ -646,15 +646,15 @@
<constraints>
<constraint firstAttribute="height" constant="360" id="dCt-K6-G5Q"/>
</constraints>
<collectionViewFlowLayout key="collectionViewLayout" scrollDirection="horizontal" minimumLineSpacing="10" minimumInteritemSpacing="10" id="q3p-JN-DhJ">
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="10" id="q3p-JN-DhJ">
<size key="itemSize" width="582" height="360"/>
<size key="headerReferenceSize" width="0.0" height="0.0"/>
<size key="footerReferenceSize" width="0.0" height="0.0"/>
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="30" maxY="0.0"/>
<inset key="sectionInset" minX="60" minY="0.0" maxX="60" maxY="0.0"/>
</collectionViewFlowLayout>
<cells>
<collectionViewCell opaque="NO" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="ReplyCell" id="gxq-A8-Ahf" customClass="ReplyCell" customModule="BilibiliLive" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="582" height="360"/>
<rect key="frame" x="60" y="0.0" width="582" height="360"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<collectionViewCellContentView key="contentView" opaque="NO" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="91g-YW-NBL">
<rect key="frame" x="0.0" y="0.0" width="582" height="360"/>
Expand Down Expand Up @@ -801,6 +801,7 @@
<outlet property="playButton" destination="lfB-VX-ver" id="9XU-MT-KOf"/>
<outlet property="playCountLabel" destination="ahl-Fa-Qpg" id="q6V-be-wNj"/>
<outlet property="recommandCollectionView" destination="nw1-cq-9yk" id="w8m-bc-mWQ"/>
<outlet property="repliesCollectionViewHeightConstraints" destination="dCt-K6-G5Q" id="ZfP-oq-I5s"/>
<outlet property="replysCollectionView" destination="zj0-CY-pLt" id="8h4-LO-bUs"/>
<outlet property="scrollView" destination="Dm8-CL-kxU" id="R6I-to-71J"/>
<outlet property="titleLabel" destination="kwE-Zo-p5T" id="3Fx-I9-hrS"/>
Expand Down Expand Up @@ -936,7 +937,7 @@
<color red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
<systemColor name="secondaryLabelColor">
<color red="0.0" green="0.0" blue="0.0" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
<color white="0.0" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>
111 changes: 111 additions & 0 deletions BilibiliLive/Component/Video/ReplyDetailViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
//
// Created by Yam on 2024/6/9.
//

import UIKit

class ReplyDetailViewController: UIViewController {
private var titleLabel: UILabel!
private var replyLabel: UILabel!
private var replyCollectionView: UICollectionView!

var reply: Replys.Reply

init(reply: Replys.Reply) {
self.reply = reply
super.init(nibName: nil, bundle: nil)
}

@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override func viewDidLoad() {
super.viewDidLoad()

setUpViews()
replyLabel.text = reply.content.message
}

// MARK: - Private

private func setUpViews() {
titleLabel = {
let label = UILabel()
self.view.addSubview(label)
label.font = .boldSystemFont(ofSize: 60)
label.text = "评论"

label.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalTo(view.safeAreaLayoutGuide)
}

return label
}()

replyLabel = {
let label = UILabel()
self.view.addSubview(label)
label.numberOfLines = 0
label.font = .preferredFont(forTextStyle: .headline)

label.snp.makeConstraints { make in
make.top.equalTo(self.titleLabel.snp.bottom).offset(60)
make.leading.equalTo(self.view.snp.leadingMargin)
make.trailing.equalTo(self.view.snp.trailingMargin)
}

return label
}()

replyCollectionView = {
let flowLayout = UICollectionViewFlowLayout()
flowLayout.itemSize = CGSize(width: 582, height: 360)
flowLayout.sectionInset = .init(top: 0, left: 60, bottom: 0, right: 60)
flowLayout.minimumLineSpacing = 10
flowLayout.minimumInteritemSpacing = 10

let collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
self.view.addSubview(collectionView)
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(UINib(nibName: ReplyCell.identifier, bundle: nil), forCellWithReuseIdentifier: ReplyCell.identifier)

collectionView.snp.makeConstraints { make in
make.leading.trailing.equalToSuperview()
make.top.equalTo(self.replyLabel.snp.bottom).offset(60)
make.bottom.equalToSuperview()
}

return collectionView
}()
}
}

extension ReplyDetailViewController: UICollectionViewDataSource, UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return reply.replies?.count ?? 0
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ReplyCell.identifier, for: indexPath) as? ReplyCell else {
fatalError("cell not found")
}

guard let reply = reply.replies?[indexPath.row] else {
fatalError("reply not found")
}

cell.config(replay: reply)

return cell
}

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
guard let reply = reply.replies?[indexPath.item] else { return }
let detail = ReplyDetailViewController(reply: reply)
present(detail, animated: true)
}
}
23 changes: 10 additions & 13 deletions BilibiliLive/Component/Video/VideoDetailViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
//

import AVKit
import Combine
import Foundation
import UIKit

Expand Down Expand Up @@ -43,6 +44,7 @@ class VideoDetailViewController: UIViewController {
@IBOutlet var pageCollectionView: UICollectionView!
@IBOutlet var recommandCollectionView: UICollectionView!
@IBOutlet var replysCollectionView: UICollectionView!
@IBOutlet var repliesCollectionViewHeightConstraints: NSLayoutConstraint!
@IBOutlet var ugcCollectionView: UICollectionView!

@IBOutlet var pageView: UIView!
Expand Down Expand Up @@ -71,6 +73,8 @@ class VideoDetailViewController: UIViewController {

private var allUgcEpisodes = [VideoDetail.Info.UgcSeason.UgcVideoInfo]()

private var subscriptions = [AnyCancellable]()

static func create(aid: Int, cid: Int?, epid: Int? = nil) -> VideoDetailViewController {
let vc = UIStoryboard(name: "Main", bundle: .main).instantiateViewController(identifier: String(describing: self)) as! VideoDetailViewController
vc.aid = aid
Expand Down Expand Up @@ -126,6 +130,11 @@ class VideoDetailViewController: UIViewController {
focusGuide.bottomAnchor.constraint(equalTo: actionButtonSpaceView.bottomAnchor),
])
focusGuide.preferredFocusEnvironments = [dislikeButton]

replysCollectionView.publisher(for: \.contentSize).sink { [weak self] newSize in
self?.repliesCollectionViewHeightConstraints.constant = newSize.height
self?.view.setNeedsLayout()
}.store(in: &subscriptions)
}

override var preferredFocusedView: UIView? {
Expand Down Expand Up @@ -456,7 +465,7 @@ extension VideoDetailViewController: UICollectionViewDelegate {
present(player, animated: true, completion: nil)
case replysCollectionView:
guard let reply = replys?.replies?[indexPath.item] else { return }
let detail = ContentDetailViewController.createReply(content: reply.content.message)
let detail = ReplyDetailViewController(reply: reply)
present(detail, animated: true)
case ugcCollectionView:
let video = allUgcEpisodes[indexPath.item]
Expand Down Expand Up @@ -576,18 +585,6 @@ extension VideoDetailViewController {
}
}

class ReplyCell: UICollectionViewCell {
@IBOutlet var avatarImageView: UIImageView!
@IBOutlet var userNameLabel: UILabel!
@IBOutlet var contenLabel: UILabel!

func config(replay: Replys.Reply) {
avatarImageView.kf.setImage(with: URL(string: replay.member.avatar), options: [.processor(DownsamplingImageProcessor(size: CGSize(width: 80, height: 80))), .processor(RoundCornerImageProcessor(radius: .widthFraction(0.5))), .cacheSerializer(FormatIndicatedCacheSerializer.png)])
userNameLabel.text = replay.member.uname
contenLabel.text = replay.content.message
}
}

class RelatedVideoCell: BLMotionCollectionViewCell {
let titleLabel = MarqueeLabel()
let imageView = UIImageView()
Expand Down
Loading

0 comments on commit 09d30ee

Please sign in to comment.