阅读页面增加推荐,阅读完成页面优化
@ -93,6 +93,11 @@
|
||||
85ACA3F92F28A7CD009D52B0 /* NRNovelReadViewModel+View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85ACA3F82F28A7C8009D52B0 /* NRNovelReadViewModel+View.swift */; };
|
||||
85B5B3982F30732800700E83 /* NRPayRetainAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85B5B3972F30732800700E83 /* NRPayRetainAlert.swift */; };
|
||||
85B5B39A2F30790600700E83 /* NRBorderLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85B5B3992F30790600700E83 /* NRBorderLabel.swift */; };
|
||||
85B5B39C2F31DAC100700E83 /* NRNovelReadRecommendViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85B5B39B2F31DAC100700E83 /* NRNovelReadRecommendViewController.swift */; };
|
||||
85B5B39E2F31F36100700E83 /* NRNovelReadFinishInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85B5B39D2F31F36100700E83 /* NRNovelReadFinishInfoView.swift */; };
|
||||
85B5B3A02F331D8700700E83 /* NRNovelReadRecommendCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85B5B39F2F331D8700700E83 /* NRNovelReadRecommendCell.swift */; };
|
||||
85B5B3A22F34378600700E83 /* NRNovelReadFinishTextCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85B5B3A12F34378600700E83 /* NRNovelReadFinishTextCell.swift */; };
|
||||
85B5B3A42F3445BC00700E83 /* NRNovelReadFinishFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85B5B3A32F3445BC00700E83 /* NRNovelReadFinishFooterView.swift */; };
|
||||
85C1786B2F050AA400A8A76E /* Poppins-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 85C178682F050AA400A8A76E /* Poppins-Medium.ttf */; };
|
||||
85C1786C2F050AA400A8A76E /* Poppins-SemiBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 85C1786A2F050AA400A8A76E /* Poppins-SemiBold.ttf */; };
|
||||
85C1786D2F050AA400A8A76E /* Poppins-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 85C178672F050AA400A8A76E /* Poppins-Bold.ttf */; };
|
||||
@ -606,6 +611,11 @@
|
||||
85ACA3F82F28A7C8009D52B0 /* NRNovelReadViewModel+View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NRNovelReadViewModel+View.swift"; sourceTree = "<group>"; };
|
||||
85B5B3972F30732800700E83 /* NRPayRetainAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRPayRetainAlert.swift; sourceTree = "<group>"; };
|
||||
85B5B3992F30790600700E83 /* NRBorderLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRBorderLabel.swift; sourceTree = "<group>"; };
|
||||
85B5B39B2F31DAC100700E83 /* NRNovelReadRecommendViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRNovelReadRecommendViewController.swift; sourceTree = "<group>"; };
|
||||
85B5B39D2F31F36100700E83 /* NRNovelReadFinishInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRNovelReadFinishInfoView.swift; sourceTree = "<group>"; };
|
||||
85B5B39F2F331D8700700E83 /* NRNovelReadRecommendCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRNovelReadRecommendCell.swift; sourceTree = "<group>"; };
|
||||
85B5B3A12F34378600700E83 /* NRNovelReadFinishTextCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRNovelReadFinishTextCell.swift; sourceTree = "<group>"; };
|
||||
85B5B3A32F3445BC00700E83 /* NRNovelReadFinishFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NRNovelReadFinishFooterView.swift; sourceTree = "<group>"; };
|
||||
85C178672F050AA400A8A76E /* Poppins-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Poppins-Bold.ttf"; sourceTree = "<group>"; };
|
||||
85C178682F050AA400A8A76E /* Poppins-Medium.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Poppins-Medium.ttf"; sourceTree = "<group>"; };
|
||||
85C178692F050AA400A8A76E /* Poppins-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Poppins-Regular.ttf"; sourceTree = "<group>"; };
|
||||
@ -2189,6 +2199,10 @@
|
||||
F34990C62EDFCE500039E939 /* NRNovelReadGradeView.swift */,
|
||||
F34990FC2EE124CF0039E939 /* NRNovelReadStarGradeView.swift */,
|
||||
F34990F82EE118FC0039E939 /* NRNovelReadFinishHeaderView.swift */,
|
||||
85B5B3A32F3445BC00700E83 /* NRNovelReadFinishFooterView.swift */,
|
||||
85B5B39D2F31F36100700E83 /* NRNovelReadFinishInfoView.swift */,
|
||||
85B5B39F2F331D8700700E83 /* NRNovelReadRecommendCell.swift */,
|
||||
85B5B3A12F34378600700E83 /* NRNovelReadFinishTextCell.swift */,
|
||||
);
|
||||
path = Reader;
|
||||
sourceTree = "<group>";
|
||||
@ -2202,6 +2216,7 @@
|
||||
F34990F42EE0346B0039E939 /* NRNovelReadBaseViewController.swift */,
|
||||
F34348F82ED855AA00AA7E70 /* NRNovelReadContentViewController.swift */,
|
||||
F34990F22EE02FD60039E939 /* NRNovelReadFinishViewController.swift */,
|
||||
85B5B39B2F31DAC100700E83 /* NRNovelReadRecommendViewController.swift */,
|
||||
);
|
||||
path = Read;
|
||||
sourceTree = "<group>";
|
||||
@ -2617,6 +2632,7 @@
|
||||
85606A922EEBB336005D989D /* NRCoinsPackConfirmItemView.swift in Sources */,
|
||||
0398106A2ED0505D0006E317 /* NRNetworkReachableManager.swift in Sources */,
|
||||
F34348C32ED6A20700AA7E70 /* NRExploreNovelContentViewController.swift in Sources */,
|
||||
85B5B3A02F331D8700700E83 /* NRNovelReadRecommendCell.swift in Sources */,
|
||||
85CF94232EEC08F4006467E3 /* NRCoinsPackClaimView.swift in Sources */,
|
||||
F34991292EE285660039E939 /* NRShowRecommendPop.swift in Sources */,
|
||||
0373D9642ED5ABBC0017DCC7 /* NREmpty.swift in Sources */,
|
||||
@ -2684,6 +2700,7 @@
|
||||
F3B859892EE97E1F0095A9CC /* NRRewardCoinsViewController.swift in Sources */,
|
||||
85CF945C2EF28433006467E3 /* NROpenAppModel.swift in Sources */,
|
||||
039810CE2ED47A130006E317 /* CGMutablePath+NRRoundedCorner.swift in Sources */,
|
||||
85B5B39C2F31DAC100700E83 /* NRNovelReadRecommendViewController.swift in Sources */,
|
||||
F34348E72ED7F91C00AA7E70 /* NSNumber+NRAdd.swift in Sources */,
|
||||
F3B8597F2EE96F810095A9CC /* NRConsumptionRecordsViewController.swift in Sources */,
|
||||
F34349122EDA84F100AA7E70 /* NRProgressView.swift in Sources */,
|
||||
@ -2836,6 +2853,7 @@
|
||||
85CF95F92EF3CC93006467E3 /* GAMRecommendThimraView.swift in Sources */,
|
||||
85CF95FA2EF3CC93006467E3 /* EMEditView.swift in Sources */,
|
||||
85CF95FB2EF3CC93006467E3 /* ITBaseController.swift in Sources */,
|
||||
85B5B3A22F34378600700E83 /* NRNovelReadFinishTextCell.swift in Sources */,
|
||||
85CF95FC2EF3CC93006467E3 /* REMustManagerCell.swift in Sources */,
|
||||
85CF95FD2EF3CC93006467E3 /* ECenterClaimView.swift in Sources */,
|
||||
85CF95FE2EF3CC93006467E3 /* AGWElyonResult.swift in Sources */,
|
||||
@ -2905,6 +2923,7 @@
|
||||
85CF963E2EF3CC93006467E3 /* NCYAterfallUnechoView.swift in Sources */,
|
||||
85CF963F2EF3CC93006467E3 /* DLTitleView.swift in Sources */,
|
||||
85CF96402EF3CC93006467E3 /* JVibeoView.swift in Sources */,
|
||||
85B5B39E2F31F36100700E83 /* NRNovelReadFinishInfoView.swift in Sources */,
|
||||
85CF96412EF3CC93006467E3 /* CJBbfdebaffdController.swift in Sources */,
|
||||
85CF96422EF3CC93006467E3 /* QMTextModuleCell.swift in Sources */,
|
||||
85CF96432EF3CC93006467E3 /* BSModityAshed.swift in Sources */,
|
||||
@ -2914,6 +2933,7 @@
|
||||
85CF96472EF3CC93006467E3 /* DRegisterEdit.swift in Sources */,
|
||||
85CF96482EF3CC93006467E3 /* LYWindowLocalizableView.swift in Sources */,
|
||||
85CF96492EF3CC93006467E3 /* HKIBrightnessView.swift in Sources */,
|
||||
85B5B3A42F3445BC00700E83 /* NRNovelReadFinishFooterView.swift in Sources */,
|
||||
85CF964A2EF3CC93006467E3 /* BMGNovelView.swift in Sources */,
|
||||
F34349282EDD815D00AA7E70 /* NRNovelCatalogCell.swift in Sources */,
|
||||
039810D22ED54F190006E317 /* NRHomeNovelListTextCell.swift in Sources */,
|
||||
|
||||
@ -261,6 +261,46 @@ struct NRNovelAPI {
|
||||
}
|
||||
}
|
||||
|
||||
static func requestReadFinishRecommand(novelId: String) async -> NRNovelModel? {
|
||||
|
||||
await withCheckedContinuation { continuation in
|
||||
var param = NRNetwork.Parameters(path: "/novel/getRecommandOneDetails")
|
||||
param.method = .get
|
||||
param.parameters = [
|
||||
"short_play_id" : novelId,
|
||||
]
|
||||
|
||||
NRNetwork.request(parameters: param) { (response: NRNetwork.Response<NRNovelModel>) in
|
||||
if response.isSuccess {
|
||||
continuation.resume(returning: response.data)
|
||||
} else {
|
||||
continuation.resume(returning: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static func requestReadRecommand(type: Int) async -> [NRNovelModel]? {
|
||||
|
||||
await withCheckedContinuation { continuation in
|
||||
var param = NRNetwork.Parameters(path: "/novel/getTypeRecommand")
|
||||
param.method = .get
|
||||
param.parameters = [
|
||||
"type" : type,
|
||||
]
|
||||
|
||||
NRNetwork.request(parameters: param) { (response: NRNetwork.Response<NRNetwork.List<NRNovelModel>>) in
|
||||
if response.isSuccess {
|
||||
continuation.resume(returning: response.data?.list)
|
||||
} else {
|
||||
continuation.resume(returning: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -8,10 +8,10 @@
|
||||
import UIKit
|
||||
|
||||
#if DEBUG
|
||||
//let NRBaseURL = "https://api-novel-test.guyantv.com"
|
||||
//let NRBaseURLPrefix = ""
|
||||
let NRBaseURL = "https://api-readerhive.readerhive.net"
|
||||
let NRBaseURLPrefix = "/readerhive"
|
||||
let NRBaseURL = "https://api-novel-test.guyantv.com"
|
||||
let NRBaseURLPrefix = ""
|
||||
//let NRBaseURL = "https://api-readerhive.readerhive.net"
|
||||
//let NRBaseURLPrefix = "/readerhive"
|
||||
#else
|
||||
let NRBaseURL = "https://api-readerhive.readerhive.net"
|
||||
let NRBaseURLPrefix = "/readerhive"
|
||||
|
||||
@ -30,6 +30,13 @@ class NRStarGradeView: UIView {
|
||||
}
|
||||
}
|
||||
|
||||
var textColor = UIColor.black {
|
||||
didSet {
|
||||
settings.textColor = textColor
|
||||
cosmosView.settings = settings
|
||||
}
|
||||
}
|
||||
|
||||
var filledImage: UIImage? = UIImage(named: "star_icon_02") {
|
||||
didSet {
|
||||
settings.filledImage = filledImage
|
||||
@ -73,7 +80,7 @@ class NRStarGradeView: UIView {
|
||||
settings.starMargin = 4
|
||||
settings.fillMode = fillMode
|
||||
settings.textFont = .font(ofSize: 12, weight: .regular)
|
||||
settings.textColor = .black
|
||||
settings.textColor = textColor
|
||||
settings.textMargin = 4
|
||||
settings.minTouchRating = 1
|
||||
settings.updateOnTouch = true
|
||||
|
||||
@ -51,6 +51,8 @@ class NRNovelModel: NSObject, SmartCodable {
|
||||
|
||||
var progress: NRNovelReadRecordModel?
|
||||
|
||||
var chapterInfo: NRReadChapterModel?
|
||||
|
||||
|
||||
static func mappingForKey() -> [SmartKeyTransformer]? {
|
||||
return [
|
||||
|
||||
@ -27,7 +27,25 @@ class NRReadChapterCatalogModel: NSObject, SmartCodable {
|
||||
|
||||
///章节详情
|
||||
@IgnoredKey
|
||||
var chapterModel: NRReadChapterModel?
|
||||
var chapterModel: NRReadChapterModel? {
|
||||
didSet {
|
||||
chapterModel?.recommandPage = recommandPage
|
||||
chapterModel?.finishPage = finishPage
|
||||
}
|
||||
}
|
||||
|
||||
@IgnoredKey
|
||||
var recommandPage: NRReadPageModel? {
|
||||
didSet {
|
||||
chapterModel?.recommandPage = recommandPage
|
||||
}
|
||||
}
|
||||
@IgnoredKey
|
||||
var finishPage: NRReadPageModel? {
|
||||
didSet {
|
||||
chapterModel?.finishPage = finishPage
|
||||
}
|
||||
}
|
||||
|
||||
///解析一个空白页面
|
||||
func parserEmpty() {
|
||||
|
||||
@ -41,7 +41,10 @@ class NRReadChapterModel: NSObject, SmartCodable {
|
||||
@IgnoredKey
|
||||
var pageCount: Int = 0
|
||||
|
||||
|
||||
@IgnoredKey
|
||||
var recommandPage: NRReadPageModel?
|
||||
@IgnoredKey
|
||||
var finishPage: NRReadPageModel?
|
||||
|
||||
|
||||
static func mappingForKey() -> [SmartKeyTransformer]? {
|
||||
@ -52,23 +55,32 @@ class NRReadChapterModel: NSObject, SmartCodable {
|
||||
|
||||
///解析数据
|
||||
func parser() {
|
||||
let tempAttributes = NRNovelReadSetManager.manager.attributes(isTitle: false, isPageing: true)
|
||||
let tempAttributes = NRNovelReadSetManager.manager.textAttributes()
|
||||
|
||||
guard !NSDictionary(dictionary: attributes).isEqual(to: tempAttributes) else { return }
|
||||
|
||||
attributes = tempAttributes
|
||||
fullContent = fullContentAttrString()
|
||||
|
||||
var tempPageModel: NRReadPageModel?
|
||||
if pageList?.last?.pageType != .textPart {
|
||||
tempPageModel = pageList?.last
|
||||
}
|
||||
|
||||
pageList = NRReadParser.pageing(attrString: fullContent, rect: NRNovelReadSetManager.manager.readRect)
|
||||
pageCount = pageList?.count ?? 0
|
||||
|
||||
if let model = tempPageModel {
|
||||
pageList?.append(model)
|
||||
if let pageModel = self.recommandPage {
|
||||
let pagePoint = pageModel.pagePoint ?? 0
|
||||
let page = Int(CGFloat(pageCount) * pagePoint)
|
||||
pageList?.insert(pageModel, at: page)
|
||||
}
|
||||
|
||||
if let pageModel = self.finishPage {
|
||||
pageList?.append(pageModel)
|
||||
}
|
||||
}
|
||||
|
||||
///只解析文本,不解析标题
|
||||
func parserText(attributes: [NSAttributedString.Key : Any] = NRNovelReadSetManager.manager.textAttributes()) {
|
||||
self.fullContent = NSMutableAttributedString(string: novel_txt ?? "", attributes: attributes)
|
||||
pageList = NRReadParser.pageing(attrString: fullContent, rect: NRNovelReadSetManager.manager.readRect)
|
||||
}
|
||||
|
||||
///解析一个空白页面
|
||||
@ -86,9 +98,9 @@ class NRReadChapterModel: NSObject, SmartCodable {
|
||||
/// 完整内容排版
|
||||
private func fullContentAttrString() ->NSMutableAttributedString {
|
||||
let name = "\n\(self.name ?? "")\n\n"
|
||||
let titleString = NSMutableAttributedString(string: name, attributes: NRNovelReadSetManager.manager.attributes(isTitle: true))
|
||||
let titleString = NSMutableAttributedString(string: name, attributes: NRNovelReadSetManager.manager.titleAttributes())
|
||||
|
||||
let contentString = NSMutableAttributedString(string: novel_txt ?? "", attributes: NRNovelReadSetManager.manager.attributes(isTitle: false))
|
||||
let contentString = NSMutableAttributedString(string: novel_txt ?? "", attributes: NRNovelReadSetManager.manager.textAttributes())
|
||||
|
||||
titleString.append(contentString)
|
||||
|
||||
|
||||
@ -12,6 +12,7 @@ class NRReadPageModel: NSObject {
|
||||
enum PageType {
|
||||
case textPart
|
||||
case readFinish
|
||||
case recommend
|
||||
}
|
||||
|
||||
var pageType: PageType = .textPart
|
||||
@ -27,11 +28,20 @@ class NRReadPageModel: NSObject {
|
||||
|
||||
var indexPath: IndexPath = IndexPath(row: 0, section: 0)
|
||||
|
||||
///用来保存特殊数据的位置信息
|
||||
///所在章节的位置
|
||||
// var chapterIndex: Int?
|
||||
///用来保存特殊数据的位置信息
|
||||
///所在章节的页面位置0-1
|
||||
var pagePoint: CGFloat?
|
||||
|
||||
var pageTitle: String?
|
||||
var recommendList: [NRNovelModel]?
|
||||
|
||||
// MARK: 快捷获取
|
||||
|
||||
/// 获取显示内容(考虑可能会变换字体颜色的情况)
|
||||
var showContent: NSAttributedString {
|
||||
let textColor = NRNovelReadSetManager.manager.textColor
|
||||
func showContent(textColor: UIColor = NRNovelReadSetManager.manager.textColor) -> NSAttributedString {
|
||||
let tempShowContent = NSMutableAttributedString(attributedString: content)
|
||||
tempShowContent.addAttributes([.foregroundColor : textColor], range: NSMakeRange(0, content.length))
|
||||
return tempShowContent
|
||||
|
||||
@ -47,7 +47,7 @@ class NRNovelReadContentBottomView: UIView {
|
||||
return label
|
||||
}()
|
||||
|
||||
private lazy var pageLabel: UILabel = {
|
||||
private(set) lazy var pageLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = .font(ofSize: 12, weight: .regular)
|
||||
label.textColor = .black
|
||||
|
||||
@ -0,0 +1,85 @@
|
||||
//
|
||||
// NRNovelReadFinishFooterView.swift
|
||||
// ReaderHive
|
||||
//
|
||||
// Created by 澜声世纪 on 2026/2/5.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SnapKit
|
||||
import YYCategories
|
||||
|
||||
class NRNovelReadFinishFooterView: UIView {
|
||||
|
||||
|
||||
var clickRefresh: (() -> Void)?
|
||||
var clickRead: (() -> Void)?
|
||||
|
||||
private lazy var refreshButton: UIButton = {
|
||||
var configuration = UIButton.Configuration.plain()
|
||||
configuration.background.cornerRadius = 24
|
||||
configuration.background.backgroundColor = .white
|
||||
configuration.image = UIImage(named: "refresh_icon_02")
|
||||
configuration.imagePadding = 8
|
||||
configuration.attributedTitle = AttributedString("reader_shuffle".localized, attributes: AttributeContainer([
|
||||
.font : UIFont.font(ofSize: 14, weight: .medium),
|
||||
.foregroundColor : UIColor.F_9710_D
|
||||
]))
|
||||
|
||||
let button = UIButton(configuration: configuration, primaryAction: UIAction(handler: { [weak self] _ in
|
||||
self?.clickRefresh?()
|
||||
}))
|
||||
return button
|
||||
}()
|
||||
|
||||
private lazy var readButton: UIButton = {
|
||||
var configuration = UIButton.Configuration.plain()
|
||||
configuration.background.cornerRadius = 24
|
||||
configuration.background.image = UIImage(named: "gradient_color_01")
|
||||
configuration.attributedTitle = AttributedString("reader_continue_reading".localized, attributes: AttributeContainer([
|
||||
.font : UIFont.font(ofSize: 14, weight: .medium),
|
||||
.foregroundColor : UIColor.white
|
||||
]))
|
||||
|
||||
let button = UIButton(configuration: configuration, primaryAction: UIAction(handler: { [weak self] _ in
|
||||
self?.clickRead?()
|
||||
}))
|
||||
|
||||
return button
|
||||
}()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
nr_setupUI()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
extension NRNovelReadFinishFooterView {
|
||||
|
||||
private func nr_setupUI() {
|
||||
addSubview(refreshButton)
|
||||
addSubview(readButton)
|
||||
|
||||
refreshButton.snp.makeConstraints { make in
|
||||
make.left.equalToSuperview().offset(16)
|
||||
make.top.equalToSuperview().offset(8)
|
||||
make.height.equalTo(48)
|
||||
make.width.equalTo(120)
|
||||
make.bottom.equalTo(-(UIScreen.safeBottom + 10))
|
||||
}
|
||||
|
||||
readButton.snp.makeConstraints { make in
|
||||
make.left.equalTo(refreshButton.snp.right).offset(12)
|
||||
make.right.equalToSuperview().offset(-16)
|
||||
make.centerY.height.equalTo(refreshButton)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -9,51 +9,30 @@ import UIKit
|
||||
import SnapKit
|
||||
import YYCategories
|
||||
|
||||
class NRNovelReadFinishHeaderView: UICollectionReusableView {
|
||||
class NRNovelReadFinishHeaderView: UIView {
|
||||
|
||||
var novelModel: NRNovelModel? {
|
||||
didSet {
|
||||
coverImageView.nr_setImage(novelModel?.image_url)
|
||||
// coverImageView.nr_setImage(novelModel?.image_url)
|
||||
gradeView.model = novelModel
|
||||
}
|
||||
}
|
||||
|
||||
var recommendNovelModel: NRNovelModel? {
|
||||
didSet {
|
||||
self.recommendInfoView.novelModel = recommendNovelModel
|
||||
self.recommendNovelNameLabel.text = recommendNovelModel?.chapterInfo?.name
|
||||
}
|
||||
}
|
||||
|
||||
var didChangeContentHeight: ((_ height: CGFloat) -> Void)?
|
||||
var clickRefresh: (() -> Void)?
|
||||
|
||||
private lazy var contentView: NRScrollView = {
|
||||
let contentView = NRScrollView()
|
||||
contentView.addObserver(self, forKeyPath: "contentSize", context: nil)
|
||||
return contentView
|
||||
}()
|
||||
|
||||
private lazy var coverBgView: UIView = {
|
||||
let view = NRGradientView()
|
||||
view.colors = [UIColor.FFC_64_A.cgColor, UIColor.FFF_7_BE.cgColor, UIColor.FFC_982.cgColor]
|
||||
view.startPoint = .init(x: 0, y: 0.5)
|
||||
view.endPoint = .init(x: 1, y: 0.5)
|
||||
view.layer.cornerRadius = 8
|
||||
view.layer.masksToBounds = true
|
||||
return view
|
||||
}()
|
||||
|
||||
private lazy var coverImageView: NRImageView = {
|
||||
let imageView = NRImageView()
|
||||
imageView.layer.cornerRadius = 7
|
||||
return imageView
|
||||
}()
|
||||
|
||||
private lazy var coverGradientLayer: CAGradientLayer = {
|
||||
let layer = CAGradientLayer()
|
||||
layer.colors = [UIColor.black.cgColor, UIColor.clear.cgColor]
|
||||
layer.startPoint = CGPoint(x: 0.5, y: 0.4)
|
||||
layer.endPoint = CGPoint(x: 0.5, y: 1.0)
|
||||
layer.cornerRadius = 8
|
||||
return layer
|
||||
}()
|
||||
|
||||
private lazy var coverDecorateView: UIView = {
|
||||
let imageView = UIImageView(image: UIImage(named: "decorate_image_01"))
|
||||
return imageView
|
||||
private lazy var scrollView: NRScrollView = {
|
||||
let scrollView = NRScrollView()
|
||||
scrollView.addObserver(self, forKeyPath: "contentSize", context: nil)
|
||||
return scrollView
|
||||
}()
|
||||
|
||||
private lazy var titleLabel: NRLabel = {
|
||||
@ -80,6 +59,9 @@ class NRNovelReadFinishHeaderView: UICollectionReusableView {
|
||||
|
||||
private lazy var gradeView: NRNovelReadStarGradeView = {
|
||||
let view = NRNovelReadStarGradeView()
|
||||
view.backgroundColor = .white
|
||||
view.layer.borderColor = UIColor.black.withAlphaComponent(0.05).cgColor
|
||||
view.layer.borderWidth = 1
|
||||
return view
|
||||
}()
|
||||
|
||||
@ -89,16 +71,24 @@ class NRNovelReadFinishHeaderView: UICollectionReusableView {
|
||||
return view
|
||||
}()
|
||||
|
||||
lazy var listTitleLabel: UILabel = {
|
||||
private lazy var recommendInfoView: NRNovelReadFinishInfoView = {
|
||||
let view = NRNovelReadFinishInfoView()
|
||||
view.clickRefresh = { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.clickRefresh?()
|
||||
}
|
||||
return view
|
||||
}()
|
||||
|
||||
private lazy var recommendNovelNameLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = .font(ofSize: 16, weight: .semibold)
|
||||
label.textColor = .black
|
||||
label.text = "reader_book_finished_list_title".localized
|
||||
return label
|
||||
}()
|
||||
|
||||
deinit {
|
||||
self.contentView.removeObserver(self, forKeyPath: "contentSize")
|
||||
self.scrollView.removeObserver(self, forKeyPath: "contentSize")
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
@ -108,8 +98,7 @@ class NRNovelReadFinishHeaderView: UICollectionReusableView {
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
coverGradientLayer.frame = coverBgView.bounds
|
||||
coverBgView.layer.mask = coverGradientLayer
|
||||
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
@ -118,8 +107,7 @@ class NRNovelReadFinishHeaderView: UICollectionReusableView {
|
||||
|
||||
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
||||
if keyPath == "contentSize" {
|
||||
let height = self.contentView.contentSize.height + 1
|
||||
self.didChangeContentHeight?(height)
|
||||
self.didChangeContentHeight?(self.scrollView.contentSize.height)
|
||||
}
|
||||
}
|
||||
|
||||
@ -128,70 +116,54 @@ class NRNovelReadFinishHeaderView: UICollectionReusableView {
|
||||
extension NRNovelReadFinishHeaderView {
|
||||
|
||||
private func nr_setupUI() {
|
||||
addSubview(contentView)
|
||||
contentView.addSubview(coverBgView)
|
||||
coverBgView.addSubview(coverImageView)
|
||||
contentView.addSubview(coverDecorateView)
|
||||
contentView.addSubview(titleLabel)
|
||||
contentView.addSubview(textLabel)
|
||||
contentView.addSubview(gradeView)
|
||||
contentView.addSubview(lineView)
|
||||
contentView.addSubview(listTitleLabel)
|
||||
addSubview(scrollView)
|
||||
scrollView.addSubview(titleLabel)
|
||||
scrollView.addSubview(textLabel)
|
||||
scrollView.addSubview(gradeView)
|
||||
scrollView.addSubview(lineView)
|
||||
scrollView.addSubview(recommendInfoView)
|
||||
scrollView.addSubview(recommendNovelNameLabel)
|
||||
|
||||
contentView.snp.makeConstraints { make in
|
||||
scrollView.snp.makeConstraints { make in
|
||||
make.edges.equalToSuperview()
|
||||
}
|
||||
|
||||
coverBgView.snp.makeConstraints { make in
|
||||
make.centerX.equalToSuperview()
|
||||
make.top.equalToSuperview().offset(16)
|
||||
make.width.equalTo(100)
|
||||
make.height.equalTo(150)
|
||||
}
|
||||
|
||||
coverImageView.snp.makeConstraints { make in
|
||||
make.left.equalToSuperview().offset(2)
|
||||
make.top.equalToSuperview().offset(2)
|
||||
make.center.equalToSuperview()
|
||||
}
|
||||
|
||||
coverDecorateView.snp.makeConstraints { make in
|
||||
make.centerX.equalToSuperview()
|
||||
make.bottom.equalTo(coverBgView).offset(12)
|
||||
}
|
||||
|
||||
titleLabel.snp.makeConstraints { make in
|
||||
make.centerX.equalToSuperview()
|
||||
make.top.equalTo(coverBgView.snp.bottom).offset(20)
|
||||
// make.right.lessThanOrEqualToSuperview().offset(-16)
|
||||
make.top.equalToSuperview().offset(2)
|
||||
make.right.lessThanOrEqualTo(gradeView)
|
||||
}
|
||||
|
||||
textLabel.snp.makeConstraints { make in
|
||||
make.centerX.equalToSuperview()
|
||||
make.top.equalTo(titleLabel.snp.bottom).offset(6)
|
||||
make.top.equalTo(titleLabel.snp.bottom).offset(5)
|
||||
make.right.lessThanOrEqualTo(gradeView)
|
||||
}
|
||||
|
||||
gradeView.snp.makeConstraints { make in
|
||||
make.left.equalToSuperview().offset(16)
|
||||
make.centerX.equalToSuperview()
|
||||
make.top.equalTo(textLabel.snp.bottom).offset(25)
|
||||
make.top.equalTo(textLabel.snp.bottom).offset(20)
|
||||
}
|
||||
|
||||
lineView.snp.makeConstraints { make in
|
||||
make.left.equalToSuperview().offset(16)
|
||||
make.centerX.equalToSuperview()
|
||||
make.top.equalTo(gradeView.snp.bottom).offset(24)
|
||||
make.top.equalTo(gradeView.snp.bottom).offset(20)
|
||||
make.height.equalTo(1)
|
||||
}
|
||||
|
||||
listTitleLabel.snp.makeConstraints { make in
|
||||
make.left.equalToSuperview().offset(16)
|
||||
make.top.equalTo(lineView.snp.bottom).offset(16)
|
||||
make.bottom.equalToSuperview().offset(-16)
|
||||
recommendInfoView.snp.makeConstraints { make in
|
||||
make.left.centerX.equalToSuperview()
|
||||
make.top.equalTo(lineView.snp.bottom)
|
||||
}
|
||||
|
||||
recommendNovelNameLabel.snp.makeConstraints { make in
|
||||
make.left.equalToSuperview().offset(16)
|
||||
make.right.lessThanOrEqualTo(recommendInfoView).offset(-16)
|
||||
make.top.equalTo(recommendInfoView.snp.bottom).offset(12)
|
||||
make.bottom.equalToSuperview().offset(-8)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
168
ReaderHive/Class/Novel/V/Reader/NRNovelReadFinishInfoView.swift
Normal file
@ -0,0 +1,168 @@
|
||||
//
|
||||
// NRNovelReadFinishInfoView.swift
|
||||
// ReaderHive
|
||||
//
|
||||
// Created by 澜声世纪 on 2026/2/3.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SnapKit
|
||||
|
||||
class NRNovelReadFinishInfoView: UIView {
|
||||
|
||||
var clickRefresh: (() -> Void)?
|
||||
|
||||
|
||||
var novelModel: NRNovelModel? {
|
||||
didSet {
|
||||
coverImageView.nr_setImage(novelModel?.image_url)
|
||||
let rate = novelModel?.rate ?? 0
|
||||
nameLabel.text = novelModel?.name
|
||||
starView.grade = rate / 2
|
||||
starView.text = NSNumber(value: Float(rate)).toString(maximumFractionDigits: 1, minimumFractionDigits: 1)
|
||||
|
||||
hotView.setNeedsUpdateConfiguration()
|
||||
tagStackView.nr_removeAllArrangedSubview()
|
||||
tagStackView.addArrangedSubview(hotView)
|
||||
|
||||
novelModel?.category?.forEach {
|
||||
guard let view = createCategoryView($0) else { return }
|
||||
tagStackView.addArrangedSubview(view)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private lazy var titleLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = .font(ofSize: 16, weight: .semibold)
|
||||
label.textColor = .black
|
||||
label.text = "reader_book_more".localized
|
||||
return label
|
||||
}()
|
||||
|
||||
private lazy var refreshButton: UIButton = {
|
||||
let button = UIButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
self.clickRefresh?()
|
||||
}))
|
||||
button.setImage(UIImage(named: "refresh_icon_01"), for: .normal)
|
||||
return button
|
||||
}()
|
||||
|
||||
private lazy var coverImageView: NRImageView = {
|
||||
let imageView = NRImageView()
|
||||
imageView.layer.cornerRadius = 4
|
||||
return imageView
|
||||
}()
|
||||
|
||||
private lazy var nameLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = .font(ofSize: 14, weight: .medium)
|
||||
label.textColor = .black
|
||||
return label
|
||||
}()
|
||||
|
||||
private lazy var starView: NRStarGradeView = {
|
||||
let view = NRStarGradeView()
|
||||
return view
|
||||
}()
|
||||
|
||||
private lazy var tagStackView: UIStackView = {
|
||||
let view = UIStackView()
|
||||
view.axis = .horizontal
|
||||
view.spacing = 8
|
||||
return view
|
||||
}()
|
||||
|
||||
private lazy var hotView: UIButton = {
|
||||
var configuration = UIButton.Configuration.plain()
|
||||
configuration.image = UIImage(named: "hot_icon_01")
|
||||
configuration.imagePadding = 1
|
||||
configuration.contentInsets = .init(top: 0, leading: 8, bottom: 0, trailing: 8)
|
||||
configuration.background.backgroundColor = .black.withAlphaComponent(0.05)
|
||||
|
||||
let button = UIButton(configuration: configuration)
|
||||
button.isUserInteractionEnabled = false
|
||||
button.layer.cornerRadius = 10
|
||||
button.layer.masksToBounds = true
|
||||
button.configurationUpdateHandler = { [weak self] button in
|
||||
guard let self = self else { return }
|
||||
let num = CGFloat(novelModel?.heats ?? 0)
|
||||
let text = NSNumber(value: num).formattedNumber()
|
||||
|
||||
button.configuration?.attributedTitle = AttributedString(text, attributes: AttributeContainer([
|
||||
.font : UIFont.font(ofSize: 10, weight: .regular),
|
||||
.foregroundColor : UIColor.black.withAlphaComponent(0.5)
|
||||
]))
|
||||
}
|
||||
return button
|
||||
}()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
tagStackView.addArrangedSubview(hotView)
|
||||
|
||||
nr_setupUI()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
private func createCategoryView(_ text: String?) -> UIView? {
|
||||
guard let text = text, text.count > 0 else { return nil }
|
||||
|
||||
let view = NRHomeCategoryTagView()
|
||||
view.text = text
|
||||
return view
|
||||
}
|
||||
}
|
||||
|
||||
extension NRNovelReadFinishInfoView {
|
||||
|
||||
private func nr_setupUI() {
|
||||
addSubview(titleLabel)
|
||||
addSubview(refreshButton)
|
||||
addSubview(coverImageView)
|
||||
addSubview(nameLabel)
|
||||
addSubview(starView)
|
||||
addSubview(tagStackView)
|
||||
|
||||
titleLabel.snp.makeConstraints { make in
|
||||
make.left.equalToSuperview().offset(16)
|
||||
make.top.equalToSuperview().offset(12)
|
||||
}
|
||||
|
||||
refreshButton.snp.makeConstraints { make in
|
||||
make.centerY.equalTo(titleLabel)
|
||||
make.right.equalToSuperview().offset(-16)
|
||||
}
|
||||
|
||||
coverImageView.snp.makeConstraints { make in
|
||||
make.left.equalToSuperview().offset(16)
|
||||
make.top.equalTo(titleLabel.snp.bottom).offset(12)
|
||||
make.width.equalTo(48)
|
||||
make.height.equalTo(72)
|
||||
make.bottom.equalToSuperview()
|
||||
}
|
||||
|
||||
nameLabel.snp.makeConstraints { make in
|
||||
make.left.equalTo(coverImageView.snp.right).offset(12)
|
||||
make.top.equalTo(coverImageView).offset(0)
|
||||
make.right.lessThanOrEqualToSuperview().offset(-16)
|
||||
}
|
||||
|
||||
starView.snp.makeConstraints { make in
|
||||
make.left.equalTo(nameLabel)
|
||||
make.top.equalTo(nameLabel.snp.bottom).offset(9)
|
||||
}
|
||||
|
||||
tagStackView.snp.makeConstraints { make in
|
||||
make.bottom.equalTo(coverImageView)
|
||||
make.left.equalTo(nameLabel)
|
||||
make.height.equalTo(20)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
//
|
||||
// NRNovelReadFinishTextCell.swift
|
||||
// ReaderHive
|
||||
//
|
||||
// Created by 澜声世纪 on 2026/2/5.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SnapKit
|
||||
|
||||
class NRNovelReadFinishTextCell: NRTableViewCell {
|
||||
|
||||
var pageModel: NRReadPageModel? {
|
||||
didSet {
|
||||
label.attributedText = pageModel?.content
|
||||
}
|
||||
}
|
||||
|
||||
private lazy var label: UILabel = {
|
||||
let label = UILabel()
|
||||
label.textColor = ._666666
|
||||
label.numberOfLines = 0
|
||||
return label
|
||||
}()
|
||||
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
nr_setupUI()
|
||||
}
|
||||
|
||||
@MainActor required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension NRNovelReadFinishTextCell {
|
||||
|
||||
private func nr_setupUI() {
|
||||
contentView.addSubview(label)
|
||||
|
||||
label.snp.makeConstraints { make in
|
||||
make.left.equalToSuperview().offset(16)
|
||||
make.right.lessThanOrEqualToSuperview().offset(-16)
|
||||
make.top.bottom.equalToSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
176
ReaderHive/Class/Novel/V/Reader/NRNovelReadRecommendCell.swift
Normal file
@ -0,0 +1,176 @@
|
||||
//
|
||||
// NRNovelReadRecommendCell.swift
|
||||
// ReaderHive
|
||||
//
|
||||
// Created by 澜声世纪 on 2026/2/4.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SnapKit
|
||||
|
||||
class NRNovelReadRecommendCell: UICollectionViewCell {
|
||||
|
||||
|
||||
var clickRead: ((_ model: NRNovelModel) -> Void)?
|
||||
|
||||
var novelModel: NRNovelModel? {
|
||||
didSet {
|
||||
coverImageView.nr_setImage(novelModel?.image_url)
|
||||
nameLabel.text = novelModel?.name
|
||||
categoryView.text = novelModel?.category?.first
|
||||
desLabel.text = novelModel?.nr_description
|
||||
|
||||
let star = novelModel?.rate ?? 0
|
||||
starView.grade = star / 2
|
||||
starView.text = NSNumber(value: star).toString(maximumFractionDigits: 1, minimumFractionDigits: 1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private lazy var coverImageView: NRImageView = {
|
||||
let imageView = NRImageView()
|
||||
imageView.layer.cornerRadius = 4
|
||||
return imageView
|
||||
}()
|
||||
|
||||
private lazy var nameLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = .font(ofSize: 14, weight: .medium)
|
||||
return label
|
||||
}()
|
||||
|
||||
private lazy var categoryView: NRHomeCategoryTagView = {
|
||||
let view = NRHomeCategoryTagView()
|
||||
return view
|
||||
}()
|
||||
|
||||
private lazy var starView: NRStarGradeView = {
|
||||
let view = NRStarGradeView()
|
||||
return view
|
||||
}()
|
||||
|
||||
private lazy var desLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = .font(ofSize: 10, weight: .regular)
|
||||
label.numberOfLines = 2
|
||||
return label
|
||||
}()
|
||||
|
||||
private lazy var readView: UILabel = {
|
||||
let label = UILabel()
|
||||
label.text = "reader_read_now".localized
|
||||
label.font = .font(ofSize: 14, weight: .medium)
|
||||
label.layer.cornerRadius = 4
|
||||
label.layer.masksToBounds = true
|
||||
label.textAlignment = .center
|
||||
return label
|
||||
}()
|
||||
|
||||
private lazy var button: UIButton = {
|
||||
let button = UIButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
guard let model = self.novelModel else { return }
|
||||
self.clickRead?(model)
|
||||
}))
|
||||
return button
|
||||
}()
|
||||
|
||||
|
||||
deinit {
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(didChangedThemeNotification), name: NRNovelReadSetManager.didChangedThemeNotification, object: nil)
|
||||
|
||||
self.contentView.layer.cornerRadius = 8
|
||||
self.contentView.layer.masksToBounds = true
|
||||
self.contentView.layer.borderWidth = 1
|
||||
|
||||
didChangedThemeNotification()
|
||||
|
||||
nr_setupUI()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
@objc private func didChangedThemeNotification() {
|
||||
if NRNovelReadSetManager.manager.isNight {
|
||||
self.contentView.layer.borderColor = UIColor.white.withAlphaComponent(0.15).cgColor
|
||||
self.contentView.backgroundColor = .black.withAlphaComponent(0.5)
|
||||
self.nameLabel.textColor = .white
|
||||
self.readView.textColor = .FFEFD_4
|
||||
self.readView.backgroundColor = .F_9710_D
|
||||
self.desLabel.textColor = .white.withAlphaComponent(0.5)
|
||||
self.starView.textColor = .white
|
||||
self.categoryView.backgroundColor = .white.withAlphaComponent(0.15)
|
||||
self.categoryView.categoryLabel.textColor = .white.withAlphaComponent(0.5)
|
||||
} else {
|
||||
self.contentView.layer.borderColor = UIColor.black.withAlphaComponent(0.05).cgColor
|
||||
self.contentView.backgroundColor = .white.withAlphaComponent(0.5)
|
||||
self.nameLabel.textColor = .black
|
||||
self.readView.textColor = .F_9710_D
|
||||
self.readView.backgroundColor = .FFEFD_4
|
||||
self.desLabel.textColor = .black.withAlphaComponent(0.5)
|
||||
self.starView.textColor = .black
|
||||
self.categoryView.backgroundColor = .black.withAlphaComponent(0.05)
|
||||
self.categoryView.categoryLabel.textColor = .black.withAlphaComponent(0.5)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension NRNovelReadRecommendCell {
|
||||
private func nr_setupUI() {
|
||||
contentView.addSubview(coverImageView)
|
||||
contentView.addSubview(nameLabel)
|
||||
contentView.addSubview(readView)
|
||||
contentView.addSubview(categoryView)
|
||||
contentView.addSubview(starView)
|
||||
contentView.addSubview(desLabel)
|
||||
contentView.addSubview(button)
|
||||
|
||||
coverImageView.snp.makeConstraints { make in
|
||||
make.left.equalToSuperview().offset(8)
|
||||
make.top.equalToSuperview().offset(8)
|
||||
make.bottom.equalToSuperview().offset(-8)
|
||||
make.width.equalTo(80)
|
||||
}
|
||||
|
||||
nameLabel.snp.makeConstraints { make in
|
||||
make.left.equalTo(coverImageView.snp.right).offset(12)
|
||||
make.right.lessThanOrEqualToSuperview().offset(-8)
|
||||
make.top.equalTo(coverImageView)
|
||||
}
|
||||
|
||||
categoryView.snp.makeConstraints { make in
|
||||
make.left.equalTo(nameLabel)
|
||||
make.top.equalTo(nameLabel.snp.bottom).offset(8)
|
||||
}
|
||||
|
||||
starView.snp.makeConstraints { make in
|
||||
make.centerY.equalTo(categoryView)
|
||||
make.left.equalTo(categoryView.snp.right).offset(8)
|
||||
}
|
||||
|
||||
desLabel.snp.makeConstraints { make in
|
||||
make.left.equalTo(nameLabel)
|
||||
make.right.lessThanOrEqualToSuperview().offset(-8)
|
||||
make.top.equalTo(categoryView.snp.bottom).offset(8)
|
||||
}
|
||||
|
||||
readView.snp.makeConstraints { make in
|
||||
make.left.equalTo(nameLabel)
|
||||
make.right.equalToSuperview().offset(-8)
|
||||
make.bottom.equalTo(coverImageView)
|
||||
make.height.equalTo(24)
|
||||
}
|
||||
|
||||
button.snp.makeConstraints { make in
|
||||
make.edges.equalToSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -45,13 +45,13 @@ class NRNovelReadContentViewController: NRNovelReadBaseViewController {
|
||||
bottomView.catalogModel = self.catalogModel
|
||||
bottomView.pageModel = self.pageModel
|
||||
|
||||
readView.content = pageModel?.showContent
|
||||
readView.content = pageModel?.showContent()
|
||||
|
||||
nr_setupUI()
|
||||
}
|
||||
|
||||
@objc private func didChangedThemeNotification() {
|
||||
readView.content = pageModel?.showContent
|
||||
readView.content = pageModel?.showContent()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -7,15 +7,12 @@
|
||||
|
||||
import UIKit
|
||||
import SnapKit
|
||||
import YYText
|
||||
|
||||
class NRNovelReadFinishViewController: NRNovelReadBaseViewController {
|
||||
|
||||
|
||||
lazy var dataArr: [NRNovelModel] = [] {
|
||||
didSet {
|
||||
self.collectionView.reloadData()
|
||||
}
|
||||
}
|
||||
private var recommandNovelModel: NRNovelModel?
|
||||
|
||||
private lazy var backButton: UIButton = {
|
||||
let button = UIButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in
|
||||
@ -27,34 +24,55 @@ class NRNovelReadFinishViewController: NRNovelReadBaseViewController {
|
||||
return button
|
||||
}()
|
||||
|
||||
private lazy var collectionViewLayout: UICollectionViewFlowLayout = {
|
||||
let itemWidth = (UIScreen.width - 32 - 40) / 3
|
||||
let itemHeight = 150 / 100 * itemWidth + 68
|
||||
|
||||
let layout = UICollectionViewFlowLayout()
|
||||
layout.itemSize = .init(width: itemWidth, height: itemHeight)
|
||||
layout.minimumLineSpacing = 18
|
||||
layout.minimumInteritemSpacing = 20
|
||||
layout.sectionInset = .init(top: 0, left: 16, bottom: 0, right: 16)
|
||||
layout.headerReferenceSize = .init(width: UIScreen.width, height: 390)
|
||||
return layout
|
||||
|
||||
|
||||
private lazy var tableView: NRTableView = {
|
||||
let tableView = NRTableView(frame: .zero, style: .plain)
|
||||
tableView.delegate = self
|
||||
tableView.dataSource = self
|
||||
tableView.separatorStyle = .none
|
||||
tableView.register(NRNovelReadFinishTextCell.self, forCellReuseIdentifier: "cell")
|
||||
return tableView
|
||||
}()
|
||||
|
||||
private lazy var collectionView: NRCollectionView = {
|
||||
let collectionView = NRCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
|
||||
collectionView.delegate = self
|
||||
collectionView.dataSource = self
|
||||
collectionView.contentInset = .init(top: 0, left: 0, bottom: UIScreen.safeBottom + 10, right: 0)
|
||||
collectionView.register(NRNovelDetailMoreLikeCell.self, forCellWithReuseIdentifier: "cell")
|
||||
collectionView.register(NRNovelReadFinishHeaderView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "header")
|
||||
return collectionView
|
||||
private lazy var headerView: NRNovelReadFinishHeaderView = {
|
||||
let view = NRNovelReadFinishHeaderView()
|
||||
view.frame = .init(x: 0, y: 0, width: UIScreen.width, height: 1)
|
||||
view.novelModel = self.viewModel?.novelModel
|
||||
view.didChangeContentHeight = { [weak self] height in
|
||||
guard let self = self else { return }
|
||||
self.tableView.tableHeaderView = nil
|
||||
self.headerView.frame = .init(x: 0, y: 0, width: UIScreen.width, height: height)
|
||||
self.tableView.tableHeaderView = self.headerView
|
||||
}
|
||||
view.clickRefresh = { [weak self] in
|
||||
self?.handleRefresh()
|
||||
}
|
||||
return view
|
||||
}()
|
||||
|
||||
private lazy var footerView: NRNovelReadFinishFooterView = {
|
||||
let view = NRNovelReadFinishFooterView(frame: .init(x: 0, y: 0, width: UIScreen.width, height: 68 + UIScreen.safeBottom))
|
||||
view.clickRefresh = { [weak self] in
|
||||
self?.handleRefresh()
|
||||
}
|
||||
view.clickRead = { [weak self] in
|
||||
guard let self = self else { return }
|
||||
guard let id = self.recommandNovelModel?.id else { return }
|
||||
let vc = NRNovelReaderViewController()
|
||||
vc.novelId = id
|
||||
vc.targetIndex = 1
|
||||
self.navigationController?.pushViewController(vc, animated: true)
|
||||
}
|
||||
return view
|
||||
}()
|
||||
|
||||
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
backgroundImageView.image = UIImage(named: "bg_image_04")
|
||||
self.view.backgroundColor = .white
|
||||
|
||||
self.view.backgroundColor = .F_2_EFEE
|
||||
|
||||
// let tap = UITapGestureRecognizer { _ in
|
||||
//
|
||||
@ -79,14 +97,42 @@ class NRNovelReadFinishViewController: NRNovelReadBaseViewController {
|
||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||
return .darkContent
|
||||
}
|
||||
|
||||
private func handleRefresh() {
|
||||
Task {
|
||||
guard await requestDataArr() else { return }
|
||||
|
||||
CATransaction.setCompletionBlock {
|
||||
self.tableView.setContentOffset(.zero, animated: true)
|
||||
}
|
||||
|
||||
CATransaction.begin()
|
||||
self.tableView.reloadData()
|
||||
CATransaction.commit()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension NRNovelReadFinishViewController {
|
||||
|
||||
private func nr_setupUI() {
|
||||
self.tableView.tableHeaderView = self.headerView
|
||||
self.tableView.tableFooterView = self.footerView
|
||||
|
||||
view.addSubview(backButton)
|
||||
view.addSubview(collectionView)
|
||||
view.addSubview(tableView)
|
||||
// scrollView.addSubview(headerView)
|
||||
// scrollView.addSubview(infoView)
|
||||
// scrollView.addSubview(novelNameLabel)
|
||||
// scrollView.addSubview(novelTextLabel)
|
||||
// scrollView.addSubview(refreshButton)
|
||||
// scrollView.addSubview(readButton)
|
||||
|
||||
// backgroundImageView.snp.remakeConstraints { make in
|
||||
// make.left.right.equalToSuperview()
|
||||
// make.top.equalTo(UIScreen.safeTop)
|
||||
// }
|
||||
|
||||
backButton.snp.makeConstraints { make in
|
||||
make.left.equalToSuperview().offset(6)
|
||||
@ -95,54 +141,81 @@ extension NRNovelReadFinishViewController {
|
||||
make.width.equalTo(36)
|
||||
}
|
||||
|
||||
collectionView.snp.makeConstraints { make in
|
||||
tableView.snp.makeConstraints { make in
|
||||
make.left.right.equalToSuperview()
|
||||
make.top.equalToSuperview().offset(UIScreen.navBarHeight)
|
||||
make.top.equalTo(backButton.snp.bottom)
|
||||
make.bottom.equalToSuperview()
|
||||
}
|
||||
|
||||
// refreshButton.snp.makeConstraints { make in
|
||||
// make.left.equalToSuperview().offset(16)
|
||||
// make.top.equalTo(novelTextLabel.snp.bottom).offset(8)
|
||||
// make.height.equalTo(48)
|
||||
// make.width.equalTo(120)
|
||||
// make.bottom.equalTo(-(UIScreen.safeBottom + 10))
|
||||
// }
|
||||
|
||||
// readButton.snp.makeConstraints { make in
|
||||
// make.left.equalTo(refreshButton.snp.right).offset(12)
|
||||
// make.right.equalTo(infoView).offset(-16)
|
||||
// make.centerY.height.equalTo(refreshButton)
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//MARK: UICollectionViewDelegate UICollectionViewDataSource
|
||||
extension NRNovelReadFinishViewController: UICollectionViewDelegate, UICollectionViewDataSource {
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! NRNovelDetailMoreLikeCell
|
||||
cell.model = self.dataArr[indexPath.row]
|
||||
//MARK: UITableViewDelegate & UITableViewDataSource
|
||||
extension NRNovelReadFinishViewController: UITableViewDelegate, UITableViewDataSource {
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! NRNovelReadFinishTextCell
|
||||
cell.pageModel = self.recommandNovelModel?.chapterInfo?.pageList?[indexPath.row]
|
||||
return cell
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
return self.dataArr.count
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return self.recommandNovelModel?.chapterInfo?.pageList?.count ?? 0
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
|
||||
let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "header", for: indexPath) as! NRNovelReadFinishHeaderView
|
||||
view.didChangeContentHeight = { [weak self] height in
|
||||
guard let self = self else { return }
|
||||
self.collectionViewLayout.headerReferenceSize = .init(width: UIScreen.width, height: height)
|
||||
}
|
||||
view.novelModel = self.viewModel?.novelModel
|
||||
return view
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||
let model = dataArr[indexPath.row]
|
||||
let vc = NRNovelDetailViewController()
|
||||
vc.novelId = model.id ?? ""
|
||||
self.viewModel?.vc?.navigationController?.pushViewController(vc, animated: true)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
extension NRNovelReadFinishViewController {
|
||||
|
||||
private func requestDataArr() async {
|
||||
guard let list = await NRNovelAPI.requestDetailRecommandData() else { return }
|
||||
self.dataArr = list
|
||||
self.collectionView.reloadData()
|
||||
private func requestDataArr() async -> Bool {
|
||||
// guard let list = await NRNovelAPI.requestDetailRecommandData() else { return }
|
||||
// self.dataArr = list
|
||||
// self.collectionView.reloadData()
|
||||
|
||||
guard let id = self.viewModel?.novelId else { return false }
|
||||
|
||||
guard let model = await NRNovelAPI.requestReadFinishRecommand(novelId: id) else { return false }
|
||||
self.recommandNovelModel = model
|
||||
let chapterModel = model.chapterInfo
|
||||
chapterModel?.parserText(attributes: [
|
||||
.font : UIFont.font(ofSize: 16, weight: .regular),
|
||||
.foregroundColor : UIColor._666666
|
||||
])
|
||||
|
||||
self.headerView.recommendNovelModel = model
|
||||
|
||||
|
||||
|
||||
self.tableView.reloadData()
|
||||
|
||||
// let text = NSAttributedString(string: chapterModel?.novel_txt ?? "", attributes: [
|
||||
// .font : UIFont.font(ofSize: 16, weight: .regular),
|
||||
// .foregroundColor : UIColor._666666
|
||||
// ])
|
||||
// let textContainer = YYTextContainer(size: .init(width: UIScreen.width - 32, height: CGFloat(MAXFLOAT)))
|
||||
// let textLayout = YYTextLayout(container: textContainer, text: text)
|
||||
//
|
||||
// self.novelTextLabel.textLayout = textLayout
|
||||
//
|
||||
// self.novelTextLabel.snp.updateConstraints { make in
|
||||
// make.height.equalTo(textLayout?.textBoundingSize.height ?? 0)
|
||||
// }
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,179 @@
|
||||
//
|
||||
// NRNovelReadRecommendViewController.swift
|
||||
// ReaderHive
|
||||
//
|
||||
// Created by 澜声世纪 on 2026/2/3.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SnapKit
|
||||
|
||||
class NRNovelReadRecommendViewController: NRNovelReadBaseViewController {
|
||||
|
||||
lazy var topView: NRNovelReadContentTopView = {
|
||||
let view = NRNovelReadContentTopView()
|
||||
return view
|
||||
}()
|
||||
|
||||
lazy var bottomView: NRNovelReadContentBottomView = {
|
||||
let view = NRNovelReadContentBottomView()
|
||||
view.pageLabel.isHidden = true
|
||||
return view
|
||||
}()
|
||||
|
||||
lazy var containerView: UIView = {
|
||||
let view = UIView()
|
||||
return view
|
||||
}()
|
||||
|
||||
private lazy var collectionViewLayout: UICollectionViewFlowLayout = {
|
||||
let layout = UICollectionViewFlowLayout()
|
||||
layout.itemSize = .init(width: UIScreen.width - 32, height: 136)
|
||||
layout.minimumLineSpacing = 16
|
||||
layout.minimumInteritemSpacing = 16
|
||||
return layout
|
||||
}()
|
||||
|
||||
private lazy var collectionView: NRCollectionView = {
|
||||
let collectionView = NRCollectionView(frame: .zero, collectionViewLayout: self.collectionViewLayout)
|
||||
collectionView.delegate = self
|
||||
collectionView.dataSource = self
|
||||
collectionView.showsVerticalScrollIndicator = false
|
||||
collectionView.register(NRNovelReadRecommendCell.self, forCellWithReuseIdentifier: "cell")
|
||||
return collectionView
|
||||
}()
|
||||
|
||||
private lazy var titleLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = .font(ofSize: 16, weight: .bold)
|
||||
return label
|
||||
}()
|
||||
|
||||
private lazy var continueButton: UIButton = {
|
||||
var configuration = UIButton.Configuration.plain()
|
||||
configuration.imagePadding = 12
|
||||
configuration.imagePlacement = .trailing
|
||||
configuration.contentInsets = .init(top: 0, leading: 0, bottom: 0, trailing: 0)
|
||||
|
||||
let button = UIButton(configuration: configuration, primaryAction: UIAction(handler: { [weak self] _ in
|
||||
|
||||
}))
|
||||
button.isUserInteractionEnabled = false
|
||||
button.configurationUpdateHandler = { [weak self] button in
|
||||
var textColor: UIColor
|
||||
if NRNovelReadSetManager.manager.isNight {
|
||||
button.configuration?.image = UIImage(named: "arrow_right_icon_08")
|
||||
textColor = .white.withAlphaComponent(0.25)
|
||||
} else {
|
||||
button.configuration?.image = UIImage(named: "arrow_right_icon_09")
|
||||
textColor = .black.withAlphaComponent(0.15)
|
||||
}
|
||||
button.configuration?.attributedTitle = AttributedString("reader_swipe_reading".localized, attributes: AttributeContainer([
|
||||
.font : UIFont.font(ofSize: 18, weight: .medium),
|
||||
.foregroundColor : textColor
|
||||
]))
|
||||
|
||||
}
|
||||
return button
|
||||
}()
|
||||
|
||||
@MainActor deinit {
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
self.backgroundImageView.isHidden = true
|
||||
self.view.backgroundColor = .clear
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(didChangedThemeNotification), name: NRNovelReadSetManager.didChangedThemeNotification, object: nil)
|
||||
|
||||
topView.viewModel = self.viewModel
|
||||
topView.catalogModel = self.catalogModel
|
||||
topView.pageModel = self.pageModel
|
||||
bottomView.viewModel = self.viewModel
|
||||
bottomView.catalogModel = self.catalogModel
|
||||
bottomView.pageModel = self.pageModel
|
||||
|
||||
self.titleLabel.text = self.pageModel?.pageTitle
|
||||
|
||||
nr_setupUI()
|
||||
|
||||
didChangedThemeNotification()
|
||||
}
|
||||
|
||||
@objc private func didChangedThemeNotification() {
|
||||
if NRNovelReadSetManager.manager.isNight {
|
||||
titleLabel.textColor = .white
|
||||
} else {
|
||||
titleLabel.textColor = .black
|
||||
}
|
||||
continueButton.setNeedsUpdateConfiguration()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension NRNovelReadRecommendViewController {
|
||||
|
||||
private func nr_setupUI() {
|
||||
view.addSubview(topView)
|
||||
view.addSubview(bottomView)
|
||||
view.addSubview(containerView)
|
||||
containerView.addSubview(collectionView)
|
||||
containerView.addSubview(titleLabel)
|
||||
containerView.addSubview(continueButton)
|
||||
|
||||
|
||||
topView.snp.makeConstraints { make in
|
||||
make.left.right.top.equalToSuperview()
|
||||
}
|
||||
|
||||
bottomView.snp.makeConstraints { make in
|
||||
make.left.right.bottom.equalToSuperview()
|
||||
}
|
||||
|
||||
containerView.snp.makeConstraints { make in
|
||||
make.left.right.equalToSuperview()
|
||||
make.top.equalTo(topView.snp.bottom)
|
||||
make.bottom.equalTo(bottomView.snp.top)
|
||||
}
|
||||
|
||||
collectionView.snp.makeConstraints { make in
|
||||
make.left.right.equalToSuperview()
|
||||
make.centerY.equalToSuperview().offset(-25)
|
||||
make.height.equalTo(collectionViewLayout.itemSize.height * 3 + collectionViewLayout.minimumLineSpacing * 2)
|
||||
}
|
||||
|
||||
titleLabel.snp.makeConstraints { make in
|
||||
make.left.equalToSuperview().offset(16)
|
||||
make.bottom.equalTo(collectionView.snp.top).offset(-16)
|
||||
}
|
||||
|
||||
continueButton.snp.makeConstraints { make in
|
||||
make.centerX.equalToSuperview()
|
||||
make.top.equalTo(collectionView.snp.bottom).offset(35)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//MARK: UICollectionViewDelegate UICollectionViewDataSource
|
||||
extension NRNovelReadRecommendViewController: UICollectionViewDelegate, UICollectionViewDataSource {
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! NRNovelReadRecommendCell
|
||||
cell.novelModel = self.pageModel?.recommendList?[indexPath.row]
|
||||
cell.clickRead = { [weak self] model in
|
||||
guard let self = self else { return }
|
||||
let vc = NRNovelReaderViewController()
|
||||
vc.novelId = model.id ?? "0"
|
||||
self.navigationController?.pushViewController(vc, animated: true)
|
||||
}
|
||||
return cell
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
return self.pageModel?.recommendList?.count ?? 0
|
||||
}
|
||||
}
|
||||
@ -168,7 +168,9 @@ extension NRNovelReaderViewController {
|
||||
guard let catalogModel = catalogModel, let pageModel = pageModel else { return nil }
|
||||
let vc: NRNovelReadBaseViewController
|
||||
if pageModel.pageType == .readFinish {
|
||||
vc = NRNovelReadFinishViewController()
|
||||
vc = self.finishVC
|
||||
} else if pageModel.pageType == .recommend {
|
||||
vc = NRNovelReadRecommendViewController()
|
||||
} else {
|
||||
vc = NRNovelReadContentViewController()
|
||||
}
|
||||
|
||||
@ -39,6 +39,15 @@ class NRNovelReaderViewController: NRViewController {
|
||||
}
|
||||
}
|
||||
|
||||
var targetIndex: Int? {
|
||||
set {
|
||||
self.viewModel.targetIndex = newValue
|
||||
}
|
||||
get {
|
||||
return self.viewModel.targetIndex
|
||||
}
|
||||
}
|
||||
|
||||
var oldBrightness: CGFloat = UIScreen.main.brightness
|
||||
|
||||
private(set) lazy var viewModel: NRNovelReadViewModel = {
|
||||
@ -70,6 +79,11 @@ class NRNovelReaderViewController: NRViewController {
|
||||
return vc
|
||||
}()
|
||||
|
||||
private(set) lazy var finishVC: NRNovelReadFinishViewController = {
|
||||
let vc = NRNovelReadFinishViewController()
|
||||
return vc
|
||||
}()
|
||||
|
||||
private lazy var readContentView: UIView = {
|
||||
let view = UIView()
|
||||
return view
|
||||
@ -142,18 +156,18 @@ class NRNovelReaderViewController: NRViewController {
|
||||
}
|
||||
|
||||
override var childForStatusBarHidden: UIViewController? {
|
||||
if let _ = self.pageViewController.viewControllers?.first as? NRNovelReadContentViewController {
|
||||
return nil
|
||||
if let vc = self.pageViewController.viewControllers?.first as? NRNovelReadFinishViewController {
|
||||
return vc
|
||||
} else {
|
||||
return self.pageViewController.viewControllers?.first
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
override var childForStatusBarStyle: UIViewController? {
|
||||
if let _ = self.pageViewController.viewControllers?.first as? NRNovelReadContentViewController {
|
||||
return nil
|
||||
if let vc = self.pageViewController.viewControllers?.first as? NRNovelReadFinishViewController {
|
||||
return vc
|
||||
} else {
|
||||
return self.pageViewController.viewControllers?.first
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@ -187,6 +201,7 @@ extension NRNovelReaderViewController {
|
||||
return;
|
||||
}
|
||||
self.view.ly_hideEmpty()
|
||||
addChild(self.pageViewController)
|
||||
|
||||
view.addSubview(readContentView)
|
||||
view.addSubview(topView)
|
||||
@ -239,6 +254,8 @@ extension NRNovelReaderViewController {
|
||||
|
||||
let currentCatalogModel = self.viewModel.chapterCatalogList[indexPath.section]
|
||||
|
||||
await self.viewModel.requestReadRecommand(type: 1)
|
||||
await self.viewModel.requestReadRecommand(type: 2)
|
||||
//获取章节数据
|
||||
await self.viewModel.requestChapterData(currentCatalogModel)
|
||||
|
||||
@ -260,7 +277,11 @@ extension NRNovelReaderViewController {
|
||||
//匹配历史记录的索引
|
||||
var section = 0
|
||||
var row = 0
|
||||
if let catalogModel = self.targetCatalogModel {
|
||||
|
||||
if let targetIndex = self.targetIndex {
|
||||
section = targetIndex
|
||||
self.targetIndex = nil
|
||||
} else if let catalogModel = self.targetCatalogModel {
|
||||
for (index, model) in self.viewModel.chapterCatalogList.enumerated() {
|
||||
if model.id == catalogModel.id {
|
||||
section = index
|
||||
|
||||
@ -105,10 +105,19 @@ extension NRNovelReadViewModel {
|
||||
currentRow = listCount - 1
|
||||
}
|
||||
if haveText {
|
||||
let currentPage = catalogModel.chapterModel?.pageList?[currentRow]
|
||||
if currentPage?.pageType != .textPart {
|
||||
var currentPage = catalogModel.chapterModel?.pageList?[currentRow]
|
||||
|
||||
while currentPage != nil, currentPage?.pageType != .textPart, currentRow > 0 {
|
||||
currentRow = currentRow - 1
|
||||
currentPage = catalogModel.chapterModel?.pageList?[currentRow]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// if currentPage?.pageType != .textPart {
|
||||
// currentRow = currentRow - 1
|
||||
// }
|
||||
}
|
||||
|
||||
self.currentPageIndexPath = IndexPath(row: currentRow, section: currentSection)
|
||||
@ -218,6 +227,8 @@ extension NRNovelReadViewModel {
|
||||
func requestChapterCatalogList() async {
|
||||
guard let list = await NRNovelAPI.requestChapterCatalogList(id: self.novelId) else { return }
|
||||
await MainActor.run {
|
||||
let model = list.last
|
||||
model?.finishPage = NRReadPageModel.createReadFinishModel()
|
||||
self.chapterCatalogList = list
|
||||
self.setEmptyData()
|
||||
}
|
||||
@ -235,13 +246,9 @@ extension NRNovelReadViewModel {
|
||||
if code == 200 {
|
||||
guard let model = model else { return nil }
|
||||
|
||||
model.parser()
|
||||
///判断为最后一个章节
|
||||
if chapterId == self.chapterCatalogList.last?.id {
|
||||
model.pageList?.append(NRReadPageModel.createReadFinishModel())
|
||||
}
|
||||
|
||||
catalogModel.chapterModel = model
|
||||
catalogModel.chapterModel?.parser()
|
||||
|
||||
if catalogModel.is_lock == true {
|
||||
catalogModel.is_lock = false
|
||||
@ -314,5 +321,27 @@ extension NRNovelReadViewModel {
|
||||
|
||||
NRStatAPI.nr_requestWatchNovelDuration(novelId: self.novelId, duration: duration, percent: Int(percent))
|
||||
}
|
||||
|
||||
func requestReadRecommand(type: Int) async {
|
||||
guard let list = await NRNovelAPI.requestReadRecommand(type: type) else { return }
|
||||
var index: Int?
|
||||
|
||||
let pageModel = NRReadPageModel()
|
||||
pageModel.pageType = .recommend
|
||||
pageModel.recommendList = list
|
||||
if type == 1 {
|
||||
index = self.chapterCatalogList.count - 2
|
||||
pageModel.pagePoint = 0.5
|
||||
pageModel.pageTitle = "reader_book_more".localized
|
||||
} else {
|
||||
index = self.chapterCatalogList.count - 1
|
||||
pageModel.pagePoint = 1
|
||||
pageModel.pageTitle = "reader_editors_choice".localized
|
||||
}
|
||||
if let index = index, index > 0 {
|
||||
let catalogModel = self.chapterCatalogList[index]
|
||||
catalogModel.recommandPage = pageModel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: 网络请求END
|
||||
|
||||
@ -17,6 +17,7 @@ class NRNovelReadViewModel: NSObject {
|
||||
@objc dynamic var novelModel: NRNovelModel?
|
||||
///目标数据,用于首次进入时,进入指定章节
|
||||
var targetCatalogModel: NRReadChapterCatalogModel?
|
||||
var targetIndex: Int?
|
||||
|
||||
weak var vc: NRNovelReaderViewController?
|
||||
weak var topView: NRNovelReadTopView?
|
||||
|
||||
@ -135,8 +135,26 @@ class NRNovelReadSetManager: NSObject {
|
||||
}
|
||||
|
||||
/// 字体属性
|
||||
/// isPaging: 为YES的时候只需要返回跟分页相关的属性即可 (原因:包含UIColor,小数点相关的...不可返回,因为无法进行比较)
|
||||
func attributes(isTitle:Bool, isPageing:Bool = false) ->[NSAttributedString.Key:Any] {
|
||||
func titleAttributes() ->[NSAttributedString.Key : Any] {
|
||||
// 段落配置
|
||||
let paragraphStyle = NSMutableParagraphStyle()
|
||||
|
||||
// 当前行间距(lineSpacing)的倍数(可根据字体大小变化修改倍数)
|
||||
paragraphStyle.lineHeightMultiple = 1.0
|
||||
|
||||
// 行间距
|
||||
paragraphStyle.lineSpacing = 0
|
||||
|
||||
// 段间距
|
||||
paragraphStyle.paragraphSpacing = 0
|
||||
|
||||
// 对其
|
||||
paragraphStyle.alignment = .center
|
||||
|
||||
return [.font: self.titleFont, .paragraphStyle: paragraphStyle]
|
||||
}
|
||||
|
||||
func textAttributes() ->[NSAttributedString.Key:Any] {
|
||||
|
||||
// 段落配置
|
||||
let paragraphStyle = NSMutableParagraphStyle()
|
||||
@ -144,51 +162,28 @@ class NRNovelReadSetManager: NSObject {
|
||||
// 当前行间距(lineSpacing)的倍数(可根据字体大小变化修改倍数)
|
||||
paragraphStyle.lineHeightMultiple = 1.0
|
||||
|
||||
if isTitle {
|
||||
|
||||
// 行间距
|
||||
paragraphStyle.lineSpacing = 0
|
||||
|
||||
// 段间距
|
||||
paragraphStyle.paragraphSpacing = 0
|
||||
|
||||
// 对其
|
||||
paragraphStyle.alignment = .center
|
||||
|
||||
}else{
|
||||
|
||||
// 行间距
|
||||
paragraphStyle.lineSpacing = lineSpacing
|
||||
|
||||
// 换行模式(避免每页尾部留空白)
|
||||
// paragraphStyle.lineBreakMode = .byCharWrapping
|
||||
paragraphStyle.lineBreakMode = .byWordWrapping
|
||||
|
||||
// 段间距
|
||||
paragraphStyle.paragraphSpacing = paragraphSpacing
|
||||
|
||||
// 对其
|
||||
paragraphStyle.alignment = .justified
|
||||
}
|
||||
|
||||
let font: UIFont
|
||||
// 行间距
|
||||
paragraphStyle.lineSpacing = lineSpacing
|
||||
|
||||
if isTitle {
|
||||
font = self.titleFont
|
||||
} else {
|
||||
font = self.textFont
|
||||
}
|
||||
// 换行模式(避免每页尾部留空白)
|
||||
// paragraphStyle.lineBreakMode = .byCharWrapping
|
||||
paragraphStyle.lineBreakMode = .byWordWrapping
|
||||
|
||||
if isPageing {
|
||||
|
||||
return [.font: font, .paragraphStyle: paragraphStyle]
|
||||
|
||||
}else{
|
||||
|
||||
return [.foregroundColor: textColor, .font: font, .paragraphStyle: paragraphStyle]
|
||||
}
|
||||
// 段间距
|
||||
paragraphStyle.paragraphSpacing = paragraphSpacing
|
||||
|
||||
// 对其
|
||||
paragraphStyle.alignment = .justified
|
||||
|
||||
|
||||
let font: UIFont = self.textFont
|
||||
|
||||
return [.font: font, .paragraphStyle: paragraphStyle]
|
||||
}
|
||||
|
||||
|
||||
|
||||
func emptyAttributes() -> [NSAttributedString.Key:Any] {
|
||||
// 段落配置
|
||||
let paragraphStyle = NSMutableParagraphStyle()
|
||||
|
||||
@ -5,12 +5,12 @@
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "阅读完成徽章@2x.png",
|
||||
"filename" : "快进,滑动@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "阅读完成徽章@3x.png",
|
||||
"filename" : "快进,滑动@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
BIN
ReaderHive/Source/Assets.xcassets/Image/arrow_right_icon_08.imageset/快进,滑动@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 343 B |
BIN
ReaderHive/Source/Assets.xcassets/Image/arrow_right_icon_08.imageset/快进,滑动@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 452 B |
22
ReaderHive/Source/Assets.xcassets/Image/arrow_right_icon_09.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "快进,滑动@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "快进,滑动@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
ReaderHive/Source/Assets.xcassets/Image/arrow_right_icon_09.imageset/快进,滑动@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 365 B |
BIN
ReaderHive/Source/Assets.xcassets/Image/arrow_right_icon_09.imageset/快进,滑动@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 489 B |
@ -5,12 +5,12 @@
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "阅读完成页背景@2x.png",
|
||||
"filename" : "阅读完成页背景2@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "阅读完成页背景@3x.png",
|
||||
"filename" : "阅读完成页背景2@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
|
||||
BIN
ReaderHive/Source/Assets.xcassets/Image/bg_image_04.imageset/阅读完成页背景2@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 101 KiB |
BIN
ReaderHive/Source/Assets.xcassets/Image/bg_image_04.imageset/阅读完成页背景2@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 191 KiB |
|
Before Width: | Height: | Size: 240 KiB |
|
Before Width: | Height: | Size: 484 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 54 KiB |
22
ReaderHive/Source/Assets.xcassets/Image/refresh_icon_01.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "刷新@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "刷新@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
ReaderHive/Source/Assets.xcassets/Image/refresh_icon_01.imageset/刷新@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 581 B |
BIN
ReaderHive/Source/Assets.xcassets/Image/refresh_icon_01.imageset/刷新@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 828 B |
22
ReaderHive/Source/Assets.xcassets/Image/refresh_icon_02.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "刷新@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "刷新@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
ReaderHive/Source/Assets.xcassets/Image/refresh_icon_02.imageset/刷新@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 781 B |
BIN
ReaderHive/Source/Assets.xcassets/Image/refresh_icon_02.imageset/刷新@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
@ -153,6 +153,10 @@
|
||||
"reader_my_vip_tips" = "Unrestricted access to all series!";
|
||||
"reader_visitor" = "Visitor";
|
||||
"free_coins" = "free coins";
|
||||
"reader_shuffle" = "Shuffle";
|
||||
"reader_continue_reading" = "Continue Reading";
|
||||
"reader_editors_choice" = "Editor's Choice for You";
|
||||
"reader_swipe_reading" = "Swipe to continue reading";
|
||||
|
||||
"vip_retain_alert_text" = "Unlock every show you love!";
|
||||
"reader_log_out_content" = "Are you sure you want to log out?";
|
||||
|
||||