mirror of
https://github.com/Aminark/legado-ios.git
synced 2026-10-24 04:41:23 +08:00
feat: 支持 EPUB 阅读并改善规则兼容与阅读稳定性
支持 EPUB 2/3 导入、目录、封面和章节插图,保留本地书管理与阅读进度。 修复 JSON 模板、动态替换与请求参数兼容,以及 RSS 翻页和连续滚动定位。 更新编辑器工具栏和回归测试,发布版本为 0.1.12(build 18)。
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
import SwiftUI
|
||||
import ImageIO
|
||||
|
||||
struct EPUBIllustrationsView: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
let book: Book
|
||||
let chapter: BookChapter
|
||||
@State private var images: [EPUBIllustration] = []
|
||||
@State private var isLoading = true
|
||||
@State private var errorMessage: String?
|
||||
|
||||
var body: some View {
|
||||
ReadingNavigationStack {
|
||||
ScrollView {
|
||||
LazyVStack(spacing: 24) {
|
||||
if isLoading {
|
||||
ProgressView("正在读取插图…").padding()
|
||||
} else if let errorMessage {
|
||||
ReadingUnavailableView("无法读取插图", systemImage: "photo", description: Text(errorMessage))
|
||||
} else if images.isEmpty {
|
||||
ReadingUnavailableView("本章没有插图", systemImage: "photo", description: Text(chapter.title))
|
||||
} else {
|
||||
ForEach(images) { image in
|
||||
VStack(spacing: 8) {
|
||||
EPUBIllustrationImage(data: image.data)
|
||||
Text("插图 \(image.id)\(image.caption.isEmpty ? "" : " · " + image.caption)")
|
||||
.font(.caption).foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
}.padding()
|
||||
}
|
||||
.background(ReadingTheme.canvas)
|
||||
.navigationTitle("本章插图")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar { ToolbarItem(placement: .confirmationAction) { Button("完成") { dismiss() } } }
|
||||
.task(id: chapter.id) {
|
||||
isLoading = true
|
||||
errorMessage = nil
|
||||
do {
|
||||
images = try await Task.detached { try LocalBookService().loadIllustrations(book: book, chapter: chapter) }.value
|
||||
} catch { errorMessage = error.localizedDescription }
|
||||
isLoading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct EPUBIllustrationImage: View {
|
||||
let data: Data
|
||||
@State private var image: UIImage?
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
if let image {
|
||||
Image(uiImage: image).resizable().scaledToFit().accessibilityLabel("书籍插图")
|
||||
} else {
|
||||
Text("图片无法显示").foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
.task {
|
||||
// Decode a bounded thumbnail off the main thread, even for huge source dimensions.
|
||||
image = await Task.detached {
|
||||
guard let source = CGImageSourceCreateWithData(data as CFData, nil),
|
||||
let thumbnail = CGImageSourceCreateThumbnailAtIndex(source, 0, [
|
||||
kCGImageSourceCreateThumbnailFromImageAlways: true,
|
||||
kCGImageSourceThumbnailMaxPixelSize: 2_048,
|
||||
kCGImageSourceCreateThumbnailWithTransform: true
|
||||
] as CFDictionary) else { return Optional<UIImage>.none }
|
||||
return UIImage(cgImage: thumbnail)
|
||||
}.value
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user