滑动返回优化

This commit is contained in:
澜声世纪 2025-12-19 17:48:16 +08:00
parent 067bba0814
commit 6ecceff7a9
25 changed files with 187 additions and 49 deletions

View File

@ -51,11 +51,13 @@ struct NRNovelAPI {
/// ///
static func requestUploadRecord(_ id: String, chapterId: String) { static func requestUploadRecord(_ id: String, chapterId: String) {
var param = NRNetwork.Parameters(path: "/novel/watchProgressReport") // var param = NRNetwork.Parameters(path: "/novel/watchProgressReport")
var param = NRNetwork.Parameters(path: "/createHistory")
param.isToast = false param.isToast = false
param.parameters = [ param.parameters = [
"short_play_id" : id, "short_play_id" : id,
"short_play_video_id" : chapterId // "short_play_video_id" : chapterId
"video_id" : chapterId
] ]
NRNetwork.request(parameters: param) { (response: NRNetwork.Response<String>) in NRNetwork.request(parameters: param) { (response: NRNetwork.Response<String>) in

View File

@ -34,10 +34,22 @@ class NRProgressView: UIView {
/// ///
private var panProgress: CGFloat = 0 private var panProgress: CGFloat = 0
var progressColor: UIColor = .black.withAlphaComponent(0.25) var progressColor: UIColor = .black.withAlphaComponent(0.25) {
var currentProgress: UIColor = .F_9710_D didSet {
setNeedsDisplay()
}
}
var currentProgress: UIColor = .F_9710_D {
didSet {
setNeedsDisplay()
}
}
var lineWidth: CGFloat = 4 var lineWidth: CGFloat = 4 {
didSet {
setNeedsDisplay()
}
}
/// ///
var isLoading = false { var isLoading = false {
@ -53,7 +65,11 @@ class NRProgressView: UIView {
} }
} }
var thumbImage: UIImage? var thumbImage: UIImage? {
didSet {
setNeedsDisplay()
}
}
var insets: UIEdgeInsets = .init(top: 0, left: 15, bottom: 0, right: 15) { var insets: UIEdgeInsets = .init(top: 0, left: 15, bottom: 0, right: 15) {
didSet { didSet {

View File

@ -7,8 +7,22 @@
import UIKit import UIKit
protocol NRScrollViewDelegate: AnyObject {
func nr_gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool?
}
extension NRScrollViewDelegate {
func nr_gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool? {
return nil
}
}
class NRScrollView: UIScrollView { class NRScrollView: UIScrollView {
weak var nr_delegate: NRScrollViewDelegate?
override init(frame: CGRect) { override init(frame: CGRect) {
super.init(frame: frame) super.init(frame: frame)
self.contentInsetAdjustmentBehavior = .never self.contentInsetAdjustmentBehavior = .never
@ -18,4 +32,24 @@ class NRScrollView: UIScrollView {
super.init(coder: coder) super.init(coder: coder)
} }
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
if let result = self.nr_delegate?.nr_gestureRecognizerShouldBegin(gestureRecognizer) {
return result
}
let result = super.gestureRecognizerShouldBegin(gestureRecognizer)
return result
}
}
class NRPagerScrollView: NRScrollView {
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
let result = super.gestureRecognizerShouldBegin(gestureRecognizer)
guard let panGestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer else { return result }
let translation = panGestureRecognizer.translation(in: self)
if translation.x > 0, self.contentOffset.x == 0 {
return false
}
return result
}
} }

View File

@ -55,7 +55,18 @@ class NRTableView: UITableView, UIGestureRecognizerDelegate {
} }
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return shouldRecognizeSimultaneously guard shouldRecognizeSimultaneously else { return false }
guard gestureRecognizer.isKind(of: UIPanGestureRecognizer.self) && otherGestureRecognizer.isKind(of: UIPanGestureRecognizer.self) else { return false }
var superview = otherGestureRecognizer.view
while superview != nil {
if superview == self {
return true
}
superview = superview?.superview
}
return false
} }
} }

View File

@ -101,6 +101,7 @@ extension NRNovelGenresCell {
categoryView.snp.makeConstraints { make in categoryView.snp.makeConstraints { make in
make.left.equalToSuperview() make.left.equalToSuperview()
make.right.lessThanOrEqualToSuperview()
make.bottom.equalToSuperview() make.bottom.equalToSuperview()
} }

View File

@ -12,12 +12,14 @@ class NRHomeCategoryTagView: UIView {
override var intrinsicContentSize: CGSize { override var intrinsicContentSize: CGSize {
return .init(width: 10, height: 20) let textWidth = self.categoryLabel.sizeThatFits(.init(width: UIScreen.width - 50, height: 20)).width
return .init(width: textWidth + 16, height: 20)
} }
var text: String? { var text: String? {
set { set {
categoryLabel.text = newValue categoryLabel.text = newValue
self.invalidateIntrinsicContentSize()
} }
get { get {
return categoryLabel.text return categoryLabel.text

View File

@ -100,6 +100,7 @@ extension NRHomeNovelListCell {
categooryView.snp.makeConstraints { make in categooryView.snp.makeConstraints { make in
make.bottom.equalToSuperview() make.bottom.equalToSuperview()
make.left.equalToSuperview() make.left.equalToSuperview()
make.right.lessThanOrEqualToSuperview()
} }
} }

View File

@ -114,6 +114,7 @@ extension NRHomeNovelListTextCell {
categoryLabel.snp.makeConstraints { make in categoryLabel.snp.makeConstraints { make in
make.left.equalTo(nameLabel) make.left.equalTo(nameLabel)
make.right.lessThanOrEqualToSuperview().offset(-8)
make.bottom.equalTo(coverImageView).offset(-7) make.bottom.equalTo(coverImageView).offset(-7)
} }
} }

View File

@ -138,6 +138,7 @@ extension NRHomeNovelMustReadTodayCell {
categoryView.snp.makeConstraints { make in categoryView.snp.makeConstraints { make in
make.left.equalTo(coverImageView.snp.right).offset(12) make.left.equalTo(coverImageView.snp.right).offset(12)
make.right.lessThanOrEqualToSuperview().offset(-12)
make.top.equalTo(coverImageView) make.top.equalTo(coverImageView)
make.height.equalTo(20) make.height.equalTo(20)
} }

View File

@ -114,6 +114,7 @@ extension NRHomeNovelNextViewCell {
categoryView.snp.makeConstraints { make in categoryView.snp.makeConstraints { make in
make.left.equalToSuperview() make.left.equalToSuperview()
make.right.lessThanOrEqualToSuperview()
// make.top.equalTo(titleLabel.snp.bottom).offset(4) // make.top.equalTo(titleLabel.snp.bottom).offset(4)
make.bottom.equalToSuperview() make.bottom.equalToSuperview()
make.height.equalTo(20) make.height.equalTo(20)

View File

@ -122,6 +122,7 @@ extension NRNovelHistoryCell {
categoryView.snp.makeConstraints { make in categoryView.snp.makeConstraints { make in
make.left.equalTo(titleLabel) make.left.equalTo(titleLabel)
make.right.lessThanOrEqualToSuperview().offset(60)
make.bottom.equalToSuperview().offset(-7.5) make.bottom.equalToSuperview().offset(-7.5)
} }

View File

@ -47,6 +47,7 @@ class NRDetailRechargeView: NRPanModalContentView {
didSet { didSet {
coinsView.shortPlayId = worksId coinsView.shortPlayId = worksId
vipView.shortPlayId = worksId vipView.shortPlayId = worksId
self.requestRestore()
} }
} }
@ -54,6 +55,7 @@ class NRDetailRechargeView: NRPanModalContentView {
didSet { didSet {
coinsView.videoId = chapterId coinsView.videoId = chapterId
vipView.videoId = chapterId vipView.videoId = chapterId
self.requestRestore()
} }
} }
@ -92,6 +94,10 @@ class NRDetailRechargeView: NRPanModalContentView {
private lazy var closeButton: UIButton = { private lazy var closeButton: UIButton = {
let button = UIButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in let button = UIButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in
guard let self = self else { return } guard let self = self else { return }
NRStatAPI.nr_requestEventStat(orderCode: nil, shortPlayId: self.worksId, videoId: self.chapterId, eventKey: .payTemplateDialog, errorMsg: nil, otherParamenters: [
"event_name" : "pay cancel"
])
Task { Task {
await self.dismiss(animated: true) await self.dismiss(animated: true)
} }
@ -226,3 +232,23 @@ extension NRDetailRechargeView {
} }
} }
extension NRDetailRechargeView {
@objc private func requestRestore() {
guard let shortPlayId = self.worksId, let videoId = self.chapterId else { return }
NRIapManager.manager.restore(isLoding: false, shortPlayId: shortPlayId, videoId: videoId) { [weak self] isFinish, buyType in
if isFinish {
Task {
await NRLoginManager.manager.updateUserInfo()
}
self?.buyFinishHandle?()
Task {
await self?.dismiss(animated: true)
}
}
}
}
}

View File

@ -19,8 +19,20 @@ extension NRNovelDetailHeaderView {
stackView.nr_removeAllArrangedSubview() stackView.nr_removeAllArrangedSubview()
model?.category?.forEach { var categoryTotalWidth: CGFloat = 0
if let view = self.createCategoryView($0) { let categoryArr = model?.category ?? []
for text in categoryArr {
if let view = self.createCategoryView(text) {
nrPrint(message: view.intrinsicContentSize)
let viewWidth = view.intrinsicContentSize.width
if categoryTotalWidth == 0 {
categoryTotalWidth += viewWidth
} else {
categoryTotalWidth += (viewWidth + self.stackView.spacing)
}
if categoryTotalWidth > UIScreen.width - 32 {
break
}
self.stackView.addArrangedSubview(view) self.stackView.addArrangedSubview(view)
} }
} }

View File

@ -42,6 +42,8 @@ class NRNovelDetailHeaderView: UIView {
heatsDataView.num = CGFloat(model?.heats ?? 0) heatsDataView.num = CGFloat(model?.heats ?? 0)
wordDataView.num = CGFloat(model?.words ?? 0) wordDataView.num = CGFloat(model?.words ?? 0)
rateDataView.num = model?.rate ?? 0 rateDataView.num = model?.rate ?? 0
contentsMoreButton.setNeedsUpdateConfiguration()
} }
} }
@ -144,9 +146,9 @@ class NRNovelDetailHeaderView: UIView {
} }
})) }))
button.isHidden = true button.isHidden = true
button.setImage(UIImage(named: "arrow_bown_icon_01"), for: .normal) button.setImage(UIImage(named: "arrow_right_icon_01"), for: .normal)
button.setImage(UIImage(named: "arrow_right_icon_01"), for: .selected) button.setImage(UIImage(named: "arrow_bown_icon_01"), for: .selected)
button.setImage(UIImage(named: "arrow_right_icon_01"), for: [.selected, .highlighted]) button.setImage(UIImage(named: "arrow_bown_icon_01"), for: [.selected, .highlighted])
return button return button
}() }()
@ -187,10 +189,6 @@ class NRNovelDetailHeaderView: UIView {
configuration.image = UIImage(named: "arrow_right_icon_04") configuration.image = UIImage(named: "arrow_right_icon_04")
configuration.imagePadding = 0 configuration.imagePadding = 0
configuration.imagePlacement = .trailing configuration.imagePlacement = .trailing
configuration.attributedTitle = AttributedString("Completed".localized, attributes: AttributeContainer([
.font : UIFont.font(ofSize: 12, weight: .regular),
.foregroundColor : UIColor.black.withAlphaComponent(0.5)
]))
let button = UIButton(configuration: configuration, primaryAction: UIAction(handler: { [weak self] _ in let button = UIButton(configuration: configuration, primaryAction: UIAction(handler: { [weak self] _ in
guard let self = self else { return } guard let self = self else { return }
@ -198,6 +196,17 @@ class NRNovelDetailHeaderView: UIView {
vc.novelModel = self.model vc.novelModel = self.model
self.viewController?.navigationController?.pushViewController(vc, animated: true) self.viewController?.navigationController?.pushViewController(vc, animated: true)
})) }))
button.configurationUpdateHandler = { [weak self] button in
guard let self = self else { return }
button.configuration?.attributedTitle = AttributedString("Completed".localized, attributes: AttributeContainer([
// button.configuration?.attributedTitle = AttributedString(self.model?.process ?? "", attributes: AttributeContainer([
.font : UIFont.font(ofSize: 12, weight: .regular),
.foregroundColor : UIColor.black.withAlphaComponent(0.5)
]))
}
return button return button
}() }()

View File

@ -265,10 +265,12 @@ class NRNovelReadBottomView: UIView {
prevButton.setTitleColor(.white, for: .normal) prevButton.setTitleColor(.white, for: .normal)
nextButton.setTitleColor(.white, for: .normal) nextButton.setTitleColor(.white, for: .normal)
contentView.backgroundColor = ._36353_A contentView.backgroundColor = ._36353_A
progressView.progressColor = .white.withAlphaComponent(0.25)
} else { } else {
prevButton.setTitleColor(.black, for: .normal) prevButton.setTitleColor(.black, for: .normal)
nextButton.setTitleColor(.black, for: .normal) nextButton.setTitleColor(.black, for: .normal)
contentView.backgroundColor = .white contentView.backgroundColor = .white
progressView.progressColor = .black.withAlphaComponent(0.25)
} }
catalogButton.setNeedsUpdateConfiguration() catalogButton.setNeedsUpdateConfiguration()
nightButton.setNeedsUpdateConfiguration() nightButton.setNeedsUpdateConfiguration()

View File

@ -18,7 +18,7 @@ class NRNovelReadGradeView: NRPanModalContentView {
nameLabel.text = model?.name nameLabel.text = model?.name
starView.grade = (model?.rate ?? 0) / 2 starView.grade = (model?.rate ?? 0) / 2
starView.text = NSNumber(value: model?.rate ?? 0).toString(maximumFractionDigits: 1) starView.text = NSNumber(value: model?.rate ?? 0).toString(maximumFractionDigits: 1, minimumFractionDigits: 1)
if let text = model?.category?.first, text.count > 0 { if let text = model?.category?.first, text.count > 0 {
categoryView.isHidden = false categoryView.isHidden = false

View File

@ -25,8 +25,8 @@ class NRNovelReadStarGradeView: UIView {
gradeView.updateOnTouch = true gradeView.updateOnTouch = true
gradeView.fillMode = .full gradeView.fillMode = .full
} else { } else {
label.text = "My Rate##".localizedReplace(text: NSNumber(value: self_rate).toString()) label.text = "My Rate##".localizedReplace(text: NSNumber(value: self_rate).toString(maximumFractionDigits: 1, minimumFractionDigits: 1))
gradeView.grade = self_rate gradeView.grade = self_rate / 2
gradeView.updateOnTouch = false gradeView.updateOnTouch = false
gradeView.fillMode = .precise gradeView.fillMode = .precise
} }
@ -84,7 +84,7 @@ class NRNovelReadStarGradeView: UIView {
guard let id = self.model?.id else { return } guard let id = self.model?.id else { return }
Task { Task {
guard await NRNovelAPI.requestRateScore(id, stars: CGFloat(grade)) else { return} guard await NRNovelAPI.requestRateScore(id, stars: CGFloat(grade)) else { return}
self.model?.self_rate = grade self.model?.self_rate = grade * 2
gradeView.isHidden = true gradeView.isHidden = true
label.isHidden = true label.isHidden = true
finishView.isHidden = false finishView.isHidden = false

View File

@ -44,8 +44,6 @@ class NRNovelReadView: UIView {
// 使 // 使
backgroundColor = UIColor.clear backgroundColor = UIColor.clear
// 便
// backgroundColor = DZM_COLOR_ARC
} }
/// ///

View File

@ -17,14 +17,13 @@ class NRNovelDetailCatalogViewController: NRViewController {
totalChaptersLabel.text = "## Chapters".localizedReplace(text: "\(novelModel?.episode_total ?? 0)") totalChaptersLabel.text = "## Chapters".localizedReplace(text: "\(novelModel?.episode_total ?? 0)")
Task {
await requestDataArr()
}
} }
} }
lazy var dataArr: [NRReadChapterCatalogModel] = [] lazy var dataArr: [NRReadChapterCatalogModel] = []
var isViewDidAppear = false
private lazy var lineView: UIView = { private lazy var lineView: UIView = {
let view = UIView() let view = UIView()
@ -70,6 +69,10 @@ class NRNovelDetailCatalogViewController: NRViewController {
configNavigationBack("arrow_left_icon_05") configNavigationBack("arrow_left_icon_05")
nr_setupUI() nr_setupUI()
Task {
await requestDataArr()
}
} }
@ -79,7 +82,17 @@ class NRNovelDetailCatalogViewController: NRViewController {
self.nr_setNavigationStyle(titleColor: UINavigationBar.titleBlackColor) self.nr_setNavigationStyle(titleColor: UINavigationBar.titleBlackColor)
} }
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if isViewDidAppear {
Task {
await requestDataArr()
}
} else {
isViewDidAppear = true
}
}
} }

View File

@ -203,11 +203,10 @@ extension NRNovelReaderViewController {
} }
let indexPath = self.indexPathToReadRecord() let indexPath = self.indexPathToReadRecord()
let section = indexPath.section
self.viewModel.currentPageIndexPath = indexPath self.viewModel.currentPageIndexPath = indexPath
let currentCatalogModel = self.viewModel.chapterCatalogList[section] let currentCatalogModel = self.viewModel.chapterCatalogList[indexPath.section]
// //
await self.viewModel.requestChapterData(currentCatalogModel) await self.viewModel.requestChapterData(currentCatalogModel)
@ -225,8 +224,6 @@ extension NRNovelReaderViewController {
// //
let recordModel = self.viewModel.getReadRecord() let recordModel = self.viewModel.getReadRecord()
var tempCatalogModel: NRReadChapterCatalogModel?
// //
var section = 0 var section = 0
var row = 0 var row = 0
@ -234,7 +231,6 @@ extension NRNovelReaderViewController {
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
tempCatalogModel = model
break break
} }
} }
@ -243,6 +239,8 @@ extension NRNovelReaderViewController {
} }
self.targetCatalogModel = nil self.targetCatalogModel = nil
} else { } else {
var tempCatalogModel: NRReadChapterCatalogModel?
if let recordModel = recordModel { if let recordModel = recordModel {
for (index, model) in self.viewModel.chapterCatalogList.enumerated() { for (index, model) in self.viewModel.chapterCatalogList.enumerated() {
if model.id == recordModel.short_play_video_id { if model.id == recordModel.short_play_video_id {
@ -254,8 +252,6 @@ extension NRNovelReaderViewController {
if let page = recordModel.page { if let page = recordModel.page {
row = page row = page
} }
}
}
// //
if tempCatalogModel?.is_lock == true { if tempCatalogModel?.is_lock == true {
@ -269,6 +265,10 @@ extension NRNovelReaderViewController {
section = tempIndex section = tempIndex
row = 0 row = 0
} }
}
}
return IndexPath(row: row, section: section) return IndexPath(row: row, section: section)
} }

View File

@ -167,7 +167,7 @@ extension NRNovelReadViewModel {
let lastTime = uploadRecordDate?.timeIntervalSince1970 ?? 0 let lastTime = uploadRecordDate?.timeIntervalSince1970 ?? 0
let nowTime = Date().timeIntervalSince1970 let nowTime = Date().timeIntervalSince1970
if lastTime == 0 || nowTime - lastTime > 5 {//5 if lastTime == 0 || nowTime - lastTime > 1 {//5
uploadRecordDate = Date() uploadRecordDate = Date()
NRNovelAPI.requestUploadRecord(novelId, chapterId: catalogModel.id ?? "") NRNovelAPI.requestUploadRecord(novelId, chapterId: catalogModel.id ?? "")
} }

View File

@ -186,6 +186,9 @@ extension NRNovelReadViewModel {
private func _openRechargeView(_ payModel: NRPayDateModel, _ catalogModel: NRReadChapterCatalogModel) { private func _openRechargeView(_ payModel: NRPayDateModel, _ catalogModel: NRReadChapterCatalogModel) {
guard self.popView == nil else { return } guard self.popView == nil else { return }
NRStatAPI.nr_requestEventStat(orderCode: nil, shortPlayId: self.novelId, videoId: catalogModel.id, eventKey: .payTemplateDialog, errorMsg: nil, otherParamenters: [
"event_name" : "pay open"
])
let view = NRDetailRechargeView() let view = NRDetailRechargeView()
view.payModel = payModel view.payModel = payModel

View File

@ -115,4 +115,8 @@ extension NROrderRecordsPageViewController: JXSegmentedListContainerViewDataSour
return self.titles.count return self.titles.count
} }
func scrollViewClass(in listContainerView: JXSegmentedListContainerView) -> AnyClass {
return NRPagerScrollView.self
}
} }

View File

@ -75,8 +75,8 @@ class NRNovelReadSetManager: NSObject {
return UIFont.font(ofSize: self.readSet.fontSize + 10, weight: .semibold) return UIFont.font(ofSize: self.readSet.fontSize + 10, weight: .semibold)
} }
var miniFontSize: CGFloat = 12 let miniFontSize: CGFloat = 12
var maxFontSize: CGFloat = 24 let maxFontSize: CGFloat = 24
var isNight: Bool { var isNight: Bool {
return readSet.isNight return readSet.isNight

View File

@ -33,7 +33,7 @@ extension CGCoins {
let lastTime = uploadRecordDate?.timeIntervalSince1970 ?? 0 let lastTime = uploadRecordDate?.timeIntervalSince1970 ?? 0
let nowTime = Date().timeIntervalSince1970 let nowTime = Date().timeIntervalSince1970
if lastTime == 0 || nowTime - lastTime > 5 { if lastTime == 0 || nowTime - lastTime > 1 {
uploadRecordDate = Date() uploadRecordDate = Date()
XMust.requestUploadRecord(novelId, chapterId: catalogModel.id ?? "") XMust.requestUploadRecord(novelId, chapterId: catalogModel.id ?? "")
} }