阅读页面增加推荐,阅读完成页面优化
@ -93,6 +93,11 @@
|
|||||||
85ACA3F92F28A7CD009D52B0 /* NRNovelReadViewModel+View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85ACA3F82F28A7C8009D52B0 /* NRNovelReadViewModel+View.swift */; };
|
85ACA3F92F28A7CD009D52B0 /* NRNovelReadViewModel+View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85ACA3F82F28A7C8009D52B0 /* NRNovelReadViewModel+View.swift */; };
|
||||||
85B5B3982F30732800700E83 /* NRPayRetainAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85B5B3972F30732800700E83 /* NRPayRetainAlert.swift */; };
|
85B5B3982F30732800700E83 /* NRPayRetainAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85B5B3972F30732800700E83 /* NRPayRetainAlert.swift */; };
|
||||||
85B5B39A2F30790600700E83 /* NRBorderLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85B5B3992F30790600700E83 /* NRBorderLabel.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 */; };
|
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 */; };
|
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 */; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
85C178692F050AA400A8A76E /* Poppins-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Poppins-Regular.ttf"; sourceTree = "<group>"; };
|
||||||
@ -2189,6 +2199,10 @@
|
|||||||
F34990C62EDFCE500039E939 /* NRNovelReadGradeView.swift */,
|
F34990C62EDFCE500039E939 /* NRNovelReadGradeView.swift */,
|
||||||
F34990FC2EE124CF0039E939 /* NRNovelReadStarGradeView.swift */,
|
F34990FC2EE124CF0039E939 /* NRNovelReadStarGradeView.swift */,
|
||||||
F34990F82EE118FC0039E939 /* NRNovelReadFinishHeaderView.swift */,
|
F34990F82EE118FC0039E939 /* NRNovelReadFinishHeaderView.swift */,
|
||||||
|
85B5B3A32F3445BC00700E83 /* NRNovelReadFinishFooterView.swift */,
|
||||||
|
85B5B39D2F31F36100700E83 /* NRNovelReadFinishInfoView.swift */,
|
||||||
|
85B5B39F2F331D8700700E83 /* NRNovelReadRecommendCell.swift */,
|
||||||
|
85B5B3A12F34378600700E83 /* NRNovelReadFinishTextCell.swift */,
|
||||||
);
|
);
|
||||||
path = Reader;
|
path = Reader;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -2202,6 +2216,7 @@
|
|||||||
F34990F42EE0346B0039E939 /* NRNovelReadBaseViewController.swift */,
|
F34990F42EE0346B0039E939 /* NRNovelReadBaseViewController.swift */,
|
||||||
F34348F82ED855AA00AA7E70 /* NRNovelReadContentViewController.swift */,
|
F34348F82ED855AA00AA7E70 /* NRNovelReadContentViewController.swift */,
|
||||||
F34990F22EE02FD60039E939 /* NRNovelReadFinishViewController.swift */,
|
F34990F22EE02FD60039E939 /* NRNovelReadFinishViewController.swift */,
|
||||||
|
85B5B39B2F31DAC100700E83 /* NRNovelReadRecommendViewController.swift */,
|
||||||
);
|
);
|
||||||
path = Read;
|
path = Read;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -2617,6 +2632,7 @@
|
|||||||
85606A922EEBB336005D989D /* NRCoinsPackConfirmItemView.swift in Sources */,
|
85606A922EEBB336005D989D /* NRCoinsPackConfirmItemView.swift in Sources */,
|
||||||
0398106A2ED0505D0006E317 /* NRNetworkReachableManager.swift in Sources */,
|
0398106A2ED0505D0006E317 /* NRNetworkReachableManager.swift in Sources */,
|
||||||
F34348C32ED6A20700AA7E70 /* NRExploreNovelContentViewController.swift in Sources */,
|
F34348C32ED6A20700AA7E70 /* NRExploreNovelContentViewController.swift in Sources */,
|
||||||
|
85B5B3A02F331D8700700E83 /* NRNovelReadRecommendCell.swift in Sources */,
|
||||||
85CF94232EEC08F4006467E3 /* NRCoinsPackClaimView.swift in Sources */,
|
85CF94232EEC08F4006467E3 /* NRCoinsPackClaimView.swift in Sources */,
|
||||||
F34991292EE285660039E939 /* NRShowRecommendPop.swift in Sources */,
|
F34991292EE285660039E939 /* NRShowRecommendPop.swift in Sources */,
|
||||||
0373D9642ED5ABBC0017DCC7 /* NREmpty.swift in Sources */,
|
0373D9642ED5ABBC0017DCC7 /* NREmpty.swift in Sources */,
|
||||||
@ -2684,6 +2700,7 @@
|
|||||||
F3B859892EE97E1F0095A9CC /* NRRewardCoinsViewController.swift in Sources */,
|
F3B859892EE97E1F0095A9CC /* NRRewardCoinsViewController.swift in Sources */,
|
||||||
85CF945C2EF28433006467E3 /* NROpenAppModel.swift in Sources */,
|
85CF945C2EF28433006467E3 /* NROpenAppModel.swift in Sources */,
|
||||||
039810CE2ED47A130006E317 /* CGMutablePath+NRRoundedCorner.swift in Sources */,
|
039810CE2ED47A130006E317 /* CGMutablePath+NRRoundedCorner.swift in Sources */,
|
||||||
|
85B5B39C2F31DAC100700E83 /* NRNovelReadRecommendViewController.swift in Sources */,
|
||||||
F34348E72ED7F91C00AA7E70 /* NSNumber+NRAdd.swift in Sources */,
|
F34348E72ED7F91C00AA7E70 /* NSNumber+NRAdd.swift in Sources */,
|
||||||
F3B8597F2EE96F810095A9CC /* NRConsumptionRecordsViewController.swift in Sources */,
|
F3B8597F2EE96F810095A9CC /* NRConsumptionRecordsViewController.swift in Sources */,
|
||||||
F34349122EDA84F100AA7E70 /* NRProgressView.swift in Sources */,
|
F34349122EDA84F100AA7E70 /* NRProgressView.swift in Sources */,
|
||||||
@ -2836,6 +2853,7 @@
|
|||||||
85CF95F92EF3CC93006467E3 /* GAMRecommendThimraView.swift in Sources */,
|
85CF95F92EF3CC93006467E3 /* GAMRecommendThimraView.swift in Sources */,
|
||||||
85CF95FA2EF3CC93006467E3 /* EMEditView.swift in Sources */,
|
85CF95FA2EF3CC93006467E3 /* EMEditView.swift in Sources */,
|
||||||
85CF95FB2EF3CC93006467E3 /* ITBaseController.swift in Sources */,
|
85CF95FB2EF3CC93006467E3 /* ITBaseController.swift in Sources */,
|
||||||
|
85B5B3A22F34378600700E83 /* NRNovelReadFinishTextCell.swift in Sources */,
|
||||||
85CF95FC2EF3CC93006467E3 /* REMustManagerCell.swift in Sources */,
|
85CF95FC2EF3CC93006467E3 /* REMustManagerCell.swift in Sources */,
|
||||||
85CF95FD2EF3CC93006467E3 /* ECenterClaimView.swift in Sources */,
|
85CF95FD2EF3CC93006467E3 /* ECenterClaimView.swift in Sources */,
|
||||||
85CF95FE2EF3CC93006467E3 /* AGWElyonResult.swift in Sources */,
|
85CF95FE2EF3CC93006467E3 /* AGWElyonResult.swift in Sources */,
|
||||||
@ -2905,6 +2923,7 @@
|
|||||||
85CF963E2EF3CC93006467E3 /* NCYAterfallUnechoView.swift in Sources */,
|
85CF963E2EF3CC93006467E3 /* NCYAterfallUnechoView.swift in Sources */,
|
||||||
85CF963F2EF3CC93006467E3 /* DLTitleView.swift in Sources */,
|
85CF963F2EF3CC93006467E3 /* DLTitleView.swift in Sources */,
|
||||||
85CF96402EF3CC93006467E3 /* JVibeoView.swift in Sources */,
|
85CF96402EF3CC93006467E3 /* JVibeoView.swift in Sources */,
|
||||||
|
85B5B39E2F31F36100700E83 /* NRNovelReadFinishInfoView.swift in Sources */,
|
||||||
85CF96412EF3CC93006467E3 /* CJBbfdebaffdController.swift in Sources */,
|
85CF96412EF3CC93006467E3 /* CJBbfdebaffdController.swift in Sources */,
|
||||||
85CF96422EF3CC93006467E3 /* QMTextModuleCell.swift in Sources */,
|
85CF96422EF3CC93006467E3 /* QMTextModuleCell.swift in Sources */,
|
||||||
85CF96432EF3CC93006467E3 /* BSModityAshed.swift in Sources */,
|
85CF96432EF3CC93006467E3 /* BSModityAshed.swift in Sources */,
|
||||||
@ -2914,6 +2933,7 @@
|
|||||||
85CF96472EF3CC93006467E3 /* DRegisterEdit.swift in Sources */,
|
85CF96472EF3CC93006467E3 /* DRegisterEdit.swift in Sources */,
|
||||||
85CF96482EF3CC93006467E3 /* LYWindowLocalizableView.swift in Sources */,
|
85CF96482EF3CC93006467E3 /* LYWindowLocalizableView.swift in Sources */,
|
||||||
85CF96492EF3CC93006467E3 /* HKIBrightnessView.swift in Sources */,
|
85CF96492EF3CC93006467E3 /* HKIBrightnessView.swift in Sources */,
|
||||||
|
85B5B3A42F3445BC00700E83 /* NRNovelReadFinishFooterView.swift in Sources */,
|
||||||
85CF964A2EF3CC93006467E3 /* BMGNovelView.swift in Sources */,
|
85CF964A2EF3CC93006467E3 /* BMGNovelView.swift in Sources */,
|
||||||
F34349282EDD815D00AA7E70 /* NRNovelCatalogCell.swift in Sources */,
|
F34349282EDD815D00AA7E70 /* NRNovelCatalogCell.swift in Sources */,
|
||||||
039810D22ED54F190006E317 /* NRHomeNovelListTextCell.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
|
import UIKit
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
//let NRBaseURL = "https://api-novel-test.guyantv.com"
|
let NRBaseURL = "https://api-novel-test.guyantv.com"
|
||||||
//let NRBaseURLPrefix = ""
|
let NRBaseURLPrefix = ""
|
||||||
let NRBaseURL = "https://api-readerhive.readerhive.net"
|
//let NRBaseURL = "https://api-readerhive.readerhive.net"
|
||||||
let NRBaseURLPrefix = "/readerhive"
|
//let NRBaseURLPrefix = "/readerhive"
|
||||||
#else
|
#else
|
||||||
let NRBaseURL = "https://api-readerhive.readerhive.net"
|
let NRBaseURL = "https://api-readerhive.readerhive.net"
|
||||||
let NRBaseURLPrefix = "/readerhive"
|
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") {
|
var filledImage: UIImage? = UIImage(named: "star_icon_02") {
|
||||||
didSet {
|
didSet {
|
||||||
settings.filledImage = filledImage
|
settings.filledImage = filledImage
|
||||||
@ -73,7 +80,7 @@ class NRStarGradeView: UIView {
|
|||||||
settings.starMargin = 4
|
settings.starMargin = 4
|
||||||
settings.fillMode = fillMode
|
settings.fillMode = fillMode
|
||||||
settings.textFont = .font(ofSize: 12, weight: .regular)
|
settings.textFont = .font(ofSize: 12, weight: .regular)
|
||||||
settings.textColor = .black
|
settings.textColor = textColor
|
||||||
settings.textMargin = 4
|
settings.textMargin = 4
|
||||||
settings.minTouchRating = 1
|
settings.minTouchRating = 1
|
||||||
settings.updateOnTouch = true
|
settings.updateOnTouch = true
|
||||||
|
|||||||
@ -51,6 +51,8 @@ class NRNovelModel: NSObject, SmartCodable {
|
|||||||
|
|
||||||
var progress: NRNovelReadRecordModel?
|
var progress: NRNovelReadRecordModel?
|
||||||
|
|
||||||
|
var chapterInfo: NRReadChapterModel?
|
||||||
|
|
||||||
|
|
||||||
static func mappingForKey() -> [SmartKeyTransformer]? {
|
static func mappingForKey() -> [SmartKeyTransformer]? {
|
||||||
return [
|
return [
|
||||||
|
|||||||
@ -27,7 +27,25 @@ class NRReadChapterCatalogModel: NSObject, SmartCodable {
|
|||||||
|
|
||||||
///章节详情
|
///章节详情
|
||||||
@IgnoredKey
|
@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() {
|
func parserEmpty() {
|
||||||
|
|||||||
@ -41,7 +41,10 @@ class NRReadChapterModel: NSObject, SmartCodable {
|
|||||||
@IgnoredKey
|
@IgnoredKey
|
||||||
var pageCount: Int = 0
|
var pageCount: Int = 0
|
||||||
|
|
||||||
|
@IgnoredKey
|
||||||
|
var recommandPage: NRReadPageModel?
|
||||||
|
@IgnoredKey
|
||||||
|
var finishPage: NRReadPageModel?
|
||||||
|
|
||||||
|
|
||||||
static func mappingForKey() -> [SmartKeyTransformer]? {
|
static func mappingForKey() -> [SmartKeyTransformer]? {
|
||||||
@ -52,23 +55,32 @@ class NRReadChapterModel: NSObject, SmartCodable {
|
|||||||
|
|
||||||
///解析数据
|
///解析数据
|
||||||
func parser() {
|
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 }
|
guard !NSDictionary(dictionary: attributes).isEqual(to: tempAttributes) else { return }
|
||||||
|
|
||||||
attributes = tempAttributes
|
attributes = tempAttributes
|
||||||
fullContent = fullContentAttrString()
|
fullContent = fullContentAttrString()
|
||||||
|
|
||||||
var tempPageModel: NRReadPageModel?
|
|
||||||
if pageList?.last?.pageType != .textPart {
|
|
||||||
tempPageModel = pageList?.last
|
|
||||||
}
|
|
||||||
pageList = NRReadParser.pageing(attrString: fullContent, rect: NRNovelReadSetManager.manager.readRect)
|
pageList = NRReadParser.pageing(attrString: fullContent, rect: NRNovelReadSetManager.manager.readRect)
|
||||||
pageCount = pageList?.count ?? 0
|
pageCount = pageList?.count ?? 0
|
||||||
|
|
||||||
if let model = tempPageModel {
|
if let pageModel = self.recommandPage {
|
||||||
pageList?.append(model)
|
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 {
|
private func fullContentAttrString() ->NSMutableAttributedString {
|
||||||
let name = "\n\(self.name ?? "")\n\n"
|
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)
|
titleString.append(contentString)
|
||||||
|
|
||||||
|
|||||||
@ -12,6 +12,7 @@ class NRReadPageModel: NSObject {
|
|||||||
enum PageType {
|
enum PageType {
|
||||||
case textPart
|
case textPart
|
||||||
case readFinish
|
case readFinish
|
||||||
|
case recommend
|
||||||
}
|
}
|
||||||
|
|
||||||
var pageType: PageType = .textPart
|
var pageType: PageType = .textPart
|
||||||
@ -27,11 +28,20 @@ class NRReadPageModel: NSObject {
|
|||||||
|
|
||||||
var indexPath: IndexPath = IndexPath(row: 0, section: 0)
|
var indexPath: IndexPath = IndexPath(row: 0, section: 0)
|
||||||
|
|
||||||
|
///用来保存特殊数据的位置信息
|
||||||
|
///所在章节的位置
|
||||||
|
// var chapterIndex: Int?
|
||||||
|
///用来保存特殊数据的位置信息
|
||||||
|
///所在章节的页面位置0-1
|
||||||
|
var pagePoint: CGFloat?
|
||||||
|
|
||||||
|
var pageTitle: String?
|
||||||
|
var recommendList: [NRNovelModel]?
|
||||||
|
|
||||||
// MARK: 快捷获取
|
// MARK: 快捷获取
|
||||||
|
|
||||||
/// 获取显示内容(考虑可能会变换字体颜色的情况)
|
/// 获取显示内容(考虑可能会变换字体颜色的情况)
|
||||||
var showContent: NSAttributedString {
|
func showContent(textColor: UIColor = NRNovelReadSetManager.manager.textColor) -> NSAttributedString {
|
||||||
let textColor = NRNovelReadSetManager.manager.textColor
|
|
||||||
let tempShowContent = NSMutableAttributedString(attributedString: content)
|
let tempShowContent = NSMutableAttributedString(attributedString: content)
|
||||||
tempShowContent.addAttributes([.foregroundColor : textColor], range: NSMakeRange(0, content.length))
|
tempShowContent.addAttributes([.foregroundColor : textColor], range: NSMakeRange(0, content.length))
|
||||||
return tempShowContent
|
return tempShowContent
|
||||||
|
|||||||
@ -47,7 +47,7 @@ class NRNovelReadContentBottomView: UIView {
|
|||||||
return label
|
return label
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private lazy var pageLabel: UILabel = {
|
private(set) lazy var pageLabel: UILabel = {
|
||||||
let label = UILabel()
|
let label = UILabel()
|
||||||
label.font = .font(ofSize: 12, weight: .regular)
|
label.font = .font(ofSize: 12, weight: .regular)
|
||||||
label.textColor = .black
|
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 SnapKit
|
||||||
import YYCategories
|
import YYCategories
|
||||||
|
|
||||||
class NRNovelReadFinishHeaderView: UICollectionReusableView {
|
class NRNovelReadFinishHeaderView: UIView {
|
||||||
|
|
||||||
var novelModel: NRNovelModel? {
|
var novelModel: NRNovelModel? {
|
||||||
didSet {
|
didSet {
|
||||||
coverImageView.nr_setImage(novelModel?.image_url)
|
// coverImageView.nr_setImage(novelModel?.image_url)
|
||||||
gradeView.model = novelModel
|
gradeView.model = novelModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var recommendNovelModel: NRNovelModel? {
|
||||||
|
didSet {
|
||||||
|
self.recommendInfoView.novelModel = recommendNovelModel
|
||||||
|
self.recommendNovelNameLabel.text = recommendNovelModel?.chapterInfo?.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var didChangeContentHeight: ((_ height: CGFloat) -> Void)?
|
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 = {
|
private lazy var scrollView: NRScrollView = {
|
||||||
let view = NRGradientView()
|
let scrollView = NRScrollView()
|
||||||
view.colors = [UIColor.FFC_64_A.cgColor, UIColor.FFF_7_BE.cgColor, UIColor.FFC_982.cgColor]
|
scrollView.addObserver(self, forKeyPath: "contentSize", context: nil)
|
||||||
view.startPoint = .init(x: 0, y: 0.5)
|
return scrollView
|
||||||
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 titleLabel: NRLabel = {
|
private lazy var titleLabel: NRLabel = {
|
||||||
@ -80,6 +59,9 @@ class NRNovelReadFinishHeaderView: UICollectionReusableView {
|
|||||||
|
|
||||||
private lazy var gradeView: NRNovelReadStarGradeView = {
|
private lazy var gradeView: NRNovelReadStarGradeView = {
|
||||||
let view = NRNovelReadStarGradeView()
|
let view = NRNovelReadStarGradeView()
|
||||||
|
view.backgroundColor = .white
|
||||||
|
view.layer.borderColor = UIColor.black.withAlphaComponent(0.05).cgColor
|
||||||
|
view.layer.borderWidth = 1
|
||||||
return view
|
return view
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -89,16 +71,24 @@ class NRNovelReadFinishHeaderView: UICollectionReusableView {
|
|||||||
return view
|
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()
|
let label = UILabel()
|
||||||
label.font = .font(ofSize: 16, weight: .semibold)
|
label.font = .font(ofSize: 16, weight: .semibold)
|
||||||
label.textColor = .black
|
label.textColor = .black
|
||||||
label.text = "reader_book_finished_list_title".localized
|
|
||||||
return label
|
return label
|
||||||
}()
|
}()
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
self.contentView.removeObserver(self, forKeyPath: "contentSize")
|
self.scrollView.removeObserver(self, forKeyPath: "contentSize")
|
||||||
}
|
}
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
@ -108,8 +98,7 @@ class NRNovelReadFinishHeaderView: UICollectionReusableView {
|
|||||||
|
|
||||||
override func layoutSubviews() {
|
override func layoutSubviews() {
|
||||||
super.layoutSubviews()
|
super.layoutSubviews()
|
||||||
coverGradientLayer.frame = coverBgView.bounds
|
|
||||||
coverBgView.layer.mask = coverGradientLayer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
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?) {
|
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
||||||
if keyPath == "contentSize" {
|
if keyPath == "contentSize" {
|
||||||
let height = self.contentView.contentSize.height + 1
|
self.didChangeContentHeight?(self.scrollView.contentSize.height)
|
||||||
self.didChangeContentHeight?(height)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,70 +116,54 @@ class NRNovelReadFinishHeaderView: UICollectionReusableView {
|
|||||||
extension NRNovelReadFinishHeaderView {
|
extension NRNovelReadFinishHeaderView {
|
||||||
|
|
||||||
private func nr_setupUI() {
|
private func nr_setupUI() {
|
||||||
addSubview(contentView)
|
addSubview(scrollView)
|
||||||
contentView.addSubview(coverBgView)
|
scrollView.addSubview(titleLabel)
|
||||||
coverBgView.addSubview(coverImageView)
|
scrollView.addSubview(textLabel)
|
||||||
contentView.addSubview(coverDecorateView)
|
scrollView.addSubview(gradeView)
|
||||||
contentView.addSubview(titleLabel)
|
scrollView.addSubview(lineView)
|
||||||
contentView.addSubview(textLabel)
|
scrollView.addSubview(recommendInfoView)
|
||||||
contentView.addSubview(gradeView)
|
scrollView.addSubview(recommendNovelNameLabel)
|
||||||
contentView.addSubview(lineView)
|
|
||||||
contentView.addSubview(listTitleLabel)
|
|
||||||
|
|
||||||
contentView.snp.makeConstraints { make in
|
scrollView.snp.makeConstraints { make in
|
||||||
make.edges.equalToSuperview()
|
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
|
titleLabel.snp.makeConstraints { make in
|
||||||
make.centerX.equalToSuperview()
|
make.centerX.equalToSuperview()
|
||||||
make.top.equalTo(coverBgView.snp.bottom).offset(20)
|
make.top.equalToSuperview().offset(2)
|
||||||
// make.right.lessThanOrEqualToSuperview().offset(-16)
|
|
||||||
make.right.lessThanOrEqualTo(gradeView)
|
make.right.lessThanOrEqualTo(gradeView)
|
||||||
}
|
}
|
||||||
|
|
||||||
textLabel.snp.makeConstraints { make in
|
textLabel.snp.makeConstraints { make in
|
||||||
make.centerX.equalToSuperview()
|
make.centerX.equalToSuperview()
|
||||||
make.top.equalTo(titleLabel.snp.bottom).offset(6)
|
make.top.equalTo(titleLabel.snp.bottom).offset(5)
|
||||||
make.right.lessThanOrEqualTo(gradeView)
|
make.right.lessThanOrEqualTo(gradeView)
|
||||||
}
|
}
|
||||||
|
|
||||||
gradeView.snp.makeConstraints { make in
|
gradeView.snp.makeConstraints { make in
|
||||||
make.left.equalToSuperview().offset(16)
|
make.left.equalToSuperview().offset(16)
|
||||||
make.centerX.equalToSuperview()
|
make.centerX.equalToSuperview()
|
||||||
make.top.equalTo(textLabel.snp.bottom).offset(25)
|
make.top.equalTo(textLabel.snp.bottom).offset(20)
|
||||||
}
|
}
|
||||||
|
|
||||||
lineView.snp.makeConstraints { make in
|
lineView.snp.makeConstraints { make in
|
||||||
make.left.equalToSuperview().offset(16)
|
make.left.equalToSuperview().offset(16)
|
||||||
make.centerX.equalToSuperview()
|
make.centerX.equalToSuperview()
|
||||||
make.top.equalTo(gradeView.snp.bottom).offset(24)
|
make.top.equalTo(gradeView.snp.bottom).offset(20)
|
||||||
make.height.equalTo(1)
|
make.height.equalTo(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
listTitleLabel.snp.makeConstraints { make in
|
recommendInfoView.snp.makeConstraints { make in
|
||||||
make.left.equalToSuperview().offset(16)
|
make.left.centerX.equalToSuperview()
|
||||||
make.top.equalTo(lineView.snp.bottom).offset(16)
|
make.top.equalTo(lineView.snp.bottom)
|
||||||
make.bottom.equalToSuperview().offset(-16)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.catalogModel = self.catalogModel
|
||||||
bottomView.pageModel = self.pageModel
|
bottomView.pageModel = self.pageModel
|
||||||
|
|
||||||
readView.content = pageModel?.showContent
|
readView.content = pageModel?.showContent()
|
||||||
|
|
||||||
nr_setupUI()
|
nr_setupUI()
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func didChangedThemeNotification() {
|
@objc private func didChangedThemeNotification() {
|
||||||
readView.content = pageModel?.showContent
|
readView.content = pageModel?.showContent()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,15 +7,12 @@
|
|||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import SnapKit
|
import SnapKit
|
||||||
|
import YYText
|
||||||
|
|
||||||
class NRNovelReadFinishViewController: NRNovelReadBaseViewController {
|
class NRNovelReadFinishViewController: NRNovelReadBaseViewController {
|
||||||
|
|
||||||
|
|
||||||
lazy var dataArr: [NRNovelModel] = [] {
|
private var recommandNovelModel: NRNovelModel?
|
||||||
didSet {
|
|
||||||
self.collectionView.reloadData()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private lazy var backButton: UIButton = {
|
private lazy var backButton: UIButton = {
|
||||||
let button = UIButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in
|
let button = UIButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in
|
||||||
@ -27,34 +24,55 @@ class NRNovelReadFinishViewController: NRNovelReadBaseViewController {
|
|||||||
return button
|
return button
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private lazy var collectionViewLayout: UICollectionViewFlowLayout = {
|
|
||||||
let itemWidth = (UIScreen.width - 32 - 40) / 3
|
|
||||||
let itemHeight = 150 / 100 * itemWidth + 68
|
private lazy var tableView: NRTableView = {
|
||||||
|
let tableView = NRTableView(frame: .zero, style: .plain)
|
||||||
let layout = UICollectionViewFlowLayout()
|
tableView.delegate = self
|
||||||
layout.itemSize = .init(width: itemWidth, height: itemHeight)
|
tableView.dataSource = self
|
||||||
layout.minimumLineSpacing = 18
|
tableView.separatorStyle = .none
|
||||||
layout.minimumInteritemSpacing = 20
|
tableView.register(NRNovelReadFinishTextCell.self, forCellReuseIdentifier: "cell")
|
||||||
layout.sectionInset = .init(top: 0, left: 16, bottom: 0, right: 16)
|
return tableView
|
||||||
layout.headerReferenceSize = .init(width: UIScreen.width, height: 390)
|
|
||||||
return layout
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private lazy var collectionView: NRCollectionView = {
|
private lazy var headerView: NRNovelReadFinishHeaderView = {
|
||||||
let collectionView = NRCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
|
let view = NRNovelReadFinishHeaderView()
|
||||||
collectionView.delegate = self
|
view.frame = .init(x: 0, y: 0, width: UIScreen.width, height: 1)
|
||||||
collectionView.dataSource = self
|
view.novelModel = self.viewModel?.novelModel
|
||||||
collectionView.contentInset = .init(top: 0, left: 0, bottom: UIScreen.safeBottom + 10, right: 0)
|
view.didChangeContentHeight = { [weak self] height in
|
||||||
collectionView.register(NRNovelDetailMoreLikeCell.self, forCellWithReuseIdentifier: "cell")
|
guard let self = self else { return }
|
||||||
collectionView.register(NRNovelReadFinishHeaderView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "header")
|
self.tableView.tableHeaderView = nil
|
||||||
return collectionView
|
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() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
backgroundImageView.image = UIImage(named: "bg_image_04")
|
backgroundImageView.image = UIImage(named: "bg_image_04")
|
||||||
self.view.backgroundColor = .white
|
self.view.backgroundColor = .F_2_EFEE
|
||||||
|
|
||||||
|
|
||||||
// let tap = UITapGestureRecognizer { _ in
|
// let tap = UITapGestureRecognizer { _ in
|
||||||
//
|
//
|
||||||
@ -79,14 +97,42 @@ class NRNovelReadFinishViewController: NRNovelReadBaseViewController {
|
|||||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||||
return .darkContent
|
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 {
|
extension NRNovelReadFinishViewController {
|
||||||
|
|
||||||
private func nr_setupUI() {
|
private func nr_setupUI() {
|
||||||
|
self.tableView.tableHeaderView = self.headerView
|
||||||
|
self.tableView.tableFooterView = self.footerView
|
||||||
|
|
||||||
view.addSubview(backButton)
|
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
|
backButton.snp.makeConstraints { make in
|
||||||
make.left.equalToSuperview().offset(6)
|
make.left.equalToSuperview().offset(6)
|
||||||
@ -95,54 +141,81 @@ extension NRNovelReadFinishViewController {
|
|||||||
make.width.equalTo(36)
|
make.width.equalTo(36)
|
||||||
}
|
}
|
||||||
|
|
||||||
collectionView.snp.makeConstraints { make in
|
tableView.snp.makeConstraints { make in
|
||||||
make.left.right.equalToSuperview()
|
make.left.right.equalToSuperview()
|
||||||
make.top.equalToSuperview().offset(UIScreen.navBarHeight)
|
make.top.equalTo(backButton.snp.bottom)
|
||||||
make.bottom.equalToSuperview()
|
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
|
//MARK: UITableViewDelegate & UITableViewDataSource
|
||||||
extension NRNovelReadFinishViewController: UICollectionViewDelegate, UICollectionViewDataSource {
|
extension NRNovelReadFinishViewController: UITableViewDelegate, UITableViewDataSource {
|
||||||
|
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||||
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! NRNovelReadFinishTextCell
|
||||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! NRNovelDetailMoreLikeCell
|
cell.pageModel = self.recommandNovelModel?.chapterInfo?.pageList?[indexPath.row]
|
||||||
cell.model = self.dataArr[indexPath.row]
|
|
||||||
return cell
|
return cell
|
||||||
}
|
}
|
||||||
|
|
||||||
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||||
return self.dataArr.count
|
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 {
|
extension NRNovelReadFinishViewController {
|
||||||
|
|
||||||
private func requestDataArr() async {
|
private func requestDataArr() async -> Bool {
|
||||||
guard let list = await NRNovelAPI.requestDetailRecommandData() else { return }
|
// guard let list = await NRNovelAPI.requestDetailRecommandData() else { return }
|
||||||
self.dataArr = list
|
// self.dataArr = list
|
||||||
self.collectionView.reloadData()
|
// 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 }
|
guard let catalogModel = catalogModel, let pageModel = pageModel else { return nil }
|
||||||
let vc: NRNovelReadBaseViewController
|
let vc: NRNovelReadBaseViewController
|
||||||
if pageModel.pageType == .readFinish {
|
if pageModel.pageType == .readFinish {
|
||||||
vc = NRNovelReadFinishViewController()
|
vc = self.finishVC
|
||||||
|
} else if pageModel.pageType == .recommend {
|
||||||
|
vc = NRNovelReadRecommendViewController()
|
||||||
} else {
|
} else {
|
||||||
vc = NRNovelReadContentViewController()
|
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
|
var oldBrightness: CGFloat = UIScreen.main.brightness
|
||||||
|
|
||||||
private(set) lazy var viewModel: NRNovelReadViewModel = {
|
private(set) lazy var viewModel: NRNovelReadViewModel = {
|
||||||
@ -70,6 +79,11 @@ class NRNovelReaderViewController: NRViewController {
|
|||||||
return vc
|
return vc
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
private(set) lazy var finishVC: NRNovelReadFinishViewController = {
|
||||||
|
let vc = NRNovelReadFinishViewController()
|
||||||
|
return vc
|
||||||
|
}()
|
||||||
|
|
||||||
private lazy var readContentView: UIView = {
|
private lazy var readContentView: UIView = {
|
||||||
let view = UIView()
|
let view = UIView()
|
||||||
return view
|
return view
|
||||||
@ -142,18 +156,18 @@ class NRNovelReaderViewController: NRViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override var childForStatusBarHidden: UIViewController? {
|
override var childForStatusBarHidden: UIViewController? {
|
||||||
if let _ = self.pageViewController.viewControllers?.first as? NRNovelReadContentViewController {
|
if let vc = self.pageViewController.viewControllers?.first as? NRNovelReadFinishViewController {
|
||||||
return nil
|
return vc
|
||||||
} else {
|
} else {
|
||||||
return self.pageViewController.viewControllers?.first
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override var childForStatusBarStyle: UIViewController? {
|
override var childForStatusBarStyle: UIViewController? {
|
||||||
if let _ = self.pageViewController.viewControllers?.first as? NRNovelReadContentViewController {
|
if let vc = self.pageViewController.viewControllers?.first as? NRNovelReadFinishViewController {
|
||||||
return nil
|
return vc
|
||||||
} else {
|
} else {
|
||||||
return self.pageViewController.viewControllers?.first
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,6 +201,7 @@ extension NRNovelReaderViewController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.view.ly_hideEmpty()
|
self.view.ly_hideEmpty()
|
||||||
|
addChild(self.pageViewController)
|
||||||
|
|
||||||
view.addSubview(readContentView)
|
view.addSubview(readContentView)
|
||||||
view.addSubview(topView)
|
view.addSubview(topView)
|
||||||
@ -239,6 +254,8 @@ extension NRNovelReaderViewController {
|
|||||||
|
|
||||||
let currentCatalogModel = self.viewModel.chapterCatalogList[indexPath.section]
|
let currentCatalogModel = self.viewModel.chapterCatalogList[indexPath.section]
|
||||||
|
|
||||||
|
await self.viewModel.requestReadRecommand(type: 1)
|
||||||
|
await self.viewModel.requestReadRecommand(type: 2)
|
||||||
//获取章节数据
|
//获取章节数据
|
||||||
await self.viewModel.requestChapterData(currentCatalogModel)
|
await self.viewModel.requestChapterData(currentCatalogModel)
|
||||||
|
|
||||||
@ -260,7 +277,11 @@ extension NRNovelReaderViewController {
|
|||||||
//匹配历史记录的索引
|
//匹配历史记录的索引
|
||||||
var section = 0
|
var section = 0
|
||||||
var row = 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() {
|
for (index, model) in self.viewModel.chapterCatalogList.enumerated() {
|
||||||
if model.id == catalogModel.id {
|
if model.id == catalogModel.id {
|
||||||
section = index
|
section = index
|
||||||
|
|||||||
@ -105,10 +105,19 @@ extension NRNovelReadViewModel {
|
|||||||
currentRow = listCount - 1
|
currentRow = listCount - 1
|
||||||
}
|
}
|
||||||
if haveText {
|
if haveText {
|
||||||
let currentPage = catalogModel.chapterModel?.pageList?[currentRow]
|
var currentPage = catalogModel.chapterModel?.pageList?[currentRow]
|
||||||
if currentPage?.pageType != .textPart {
|
|
||||||
|
while currentPage != nil, currentPage?.pageType != .textPart, currentRow > 0 {
|
||||||
currentRow = currentRow - 1
|
currentRow = currentRow - 1
|
||||||
|
currentPage = catalogModel.chapterModel?.pageList?[currentRow]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// if currentPage?.pageType != .textPart {
|
||||||
|
// currentRow = currentRow - 1
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
self.currentPageIndexPath = IndexPath(row: currentRow, section: currentSection)
|
self.currentPageIndexPath = IndexPath(row: currentRow, section: currentSection)
|
||||||
@ -218,6 +227,8 @@ extension NRNovelReadViewModel {
|
|||||||
func requestChapterCatalogList() async {
|
func requestChapterCatalogList() async {
|
||||||
guard let list = await NRNovelAPI.requestChapterCatalogList(id: self.novelId) else { return }
|
guard let list = await NRNovelAPI.requestChapterCatalogList(id: self.novelId) else { return }
|
||||||
await MainActor.run {
|
await MainActor.run {
|
||||||
|
let model = list.last
|
||||||
|
model?.finishPage = NRReadPageModel.createReadFinishModel()
|
||||||
self.chapterCatalogList = list
|
self.chapterCatalogList = list
|
||||||
self.setEmptyData()
|
self.setEmptyData()
|
||||||
}
|
}
|
||||||
@ -235,13 +246,9 @@ extension NRNovelReadViewModel {
|
|||||||
if code == 200 {
|
if code == 200 {
|
||||||
guard let model = model else { return nil }
|
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 = model
|
||||||
|
catalogModel.chapterModel?.parser()
|
||||||
|
|
||||||
if catalogModel.is_lock == true {
|
if catalogModel.is_lock == true {
|
||||||
catalogModel.is_lock = false
|
catalogModel.is_lock = false
|
||||||
@ -314,5 +321,27 @@ extension NRNovelReadViewModel {
|
|||||||
|
|
||||||
NRStatAPI.nr_requestWatchNovelDuration(novelId: self.novelId, duration: duration, percent: Int(percent))
|
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?
|
@objc dynamic var novelModel: NRNovelModel?
|
||||||
///目标数据,用于首次进入时,进入指定章节
|
///目标数据,用于首次进入时,进入指定章节
|
||||||
var targetCatalogModel: NRReadChapterCatalogModel?
|
var targetCatalogModel: NRReadChapterCatalogModel?
|
||||||
|
var targetIndex: Int?
|
||||||
|
|
||||||
weak var vc: NRNovelReaderViewController?
|
weak var vc: NRNovelReaderViewController?
|
||||||
weak var topView: NRNovelReadTopView?
|
weak var topView: NRNovelReadTopView?
|
||||||
|
|||||||
@ -135,8 +135,26 @@ class NRNovelReadSetManager: NSObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 字体属性
|
/// 字体属性
|
||||||
/// isPaging: 为YES的时候只需要返回跟分页相关的属性即可 (原因:包含UIColor,小数点相关的...不可返回,因为无法进行比较)
|
func titleAttributes() ->[NSAttributedString.Key : Any] {
|
||||||
func attributes(isTitle:Bool, isPageing:Bool = false) ->[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()
|
let paragraphStyle = NSMutableParagraphStyle()
|
||||||
@ -144,51 +162,28 @@ class NRNovelReadSetManager: NSObject {
|
|||||||
// 当前行间距(lineSpacing)的倍数(可根据字体大小变化修改倍数)
|
// 当前行间距(lineSpacing)的倍数(可根据字体大小变化修改倍数)
|
||||||
paragraphStyle.lineHeightMultiple = 1.0
|
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
|
// paragraphStyle.lineBreakMode = .byCharWrapping
|
||||||
} else {
|
paragraphStyle.lineBreakMode = .byWordWrapping
|
||||||
font = self.textFont
|
|
||||||
}
|
|
||||||
|
|
||||||
if isPageing {
|
// 段间距
|
||||||
|
paragraphStyle.paragraphSpacing = paragraphSpacing
|
||||||
return [.font: font, .paragraphStyle: paragraphStyle]
|
|
||||||
|
// 对其
|
||||||
}else{
|
paragraphStyle.alignment = .justified
|
||||||
|
|
||||||
return [.foregroundColor: textColor, .font: font, .paragraphStyle: paragraphStyle]
|
|
||||||
}
|
let font: UIFont = self.textFont
|
||||||
|
|
||||||
|
return [.font: font, .paragraphStyle: paragraphStyle]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func emptyAttributes() -> [NSAttributedString.Key:Any] {
|
func emptyAttributes() -> [NSAttributedString.Key:Any] {
|
||||||
// 段落配置
|
// 段落配置
|
||||||
let paragraphStyle = NSMutableParagraphStyle()
|
let paragraphStyle = NSMutableParagraphStyle()
|
||||||
|
|||||||
@ -5,12 +5,12 @@
|
|||||||
"scale" : "1x"
|
"scale" : "1x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "阅读完成徽章@2x.png",
|
"filename" : "快进,滑动@2x.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "阅读完成徽章@3x.png",
|
"filename" : "快进,滑动@3x.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "3x"
|
"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"
|
"scale" : "1x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "阅读完成页背景@2x.png",
|
"filename" : "阅读完成页背景2@2x.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "阅读完成页背景@3x.png",
|
"filename" : "阅读完成页背景2@3x.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "3x"
|
"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_my_vip_tips" = "Unrestricted access to all series!";
|
||||||
"reader_visitor" = "Visitor";
|
"reader_visitor" = "Visitor";
|
||||||
"free_coins" = "free coins";
|
"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!";
|
"vip_retain_alert_text" = "Unlock every show you love!";
|
||||||
"reader_log_out_content" = "Are you sure you want to log out?";
|
"reader_log_out_content" = "Are you sure you want to log out?";
|
||||||
|
|||||||