版本记录
版本号 | 时间 |
---|---|
V1.0 | 2021.03.22 星期一 |
前言
QuickLook框架提供了文档的预览功能。接下来几篇我们就一起看一下这个框架。感兴趣的可以看下面几篇。
1. QuickLook框架详细解析(一) —— 基本概览(一)
2. QuickLook框架详细解析(二) —— QuickLook 预览简单示例(一)
3. QuickLook框架详细解析(三) —— QuickLook 预览简单示例(二)
4. QuickLook框架详细解析(四) —— QuickLook预览和缩略图扩展的实现(一)
源码
1. Swift
首先看下工程组织文件
下面就是源码了
1. AppMain.swift
import SwiftUI
@main
struct AppMain: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
2. ContentView.swift
import SwiftUI
struct ContentView: View {
let documents = Document.documents
let title = "Thumbs"
var body: some View {
NavigationView {
ScrollView(.vertical) {
LazyVGrid(
columns: [GridItem(.adaptive(minimum: 150, maximum: 150), spacing: 44)],
spacing: 10
) {
ForEach(documents) { document in
NavigationLink(destination: DocumentPreviewView(document: document)) {
DocumentThumbnailView(document: document)
}
}
}
}
.navigationBarTitle(title)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
3. Document.swift
import Foundation
import QuickLook
struct Document {
let url: URL
var name: String {
url.lastPathComponent
}
}
extension Document: Identifiable {
var id: URL {
url
}
}
// MARK: - Static helper methods
extension Document {
static let documents = [
Bundle.main.url(forResource: "zombiethumb", withExtension: "jpg"),
Bundle.main.url(forResource: "humanthumb", withExtension: "pdf"),
Bundle.main.url(forResource: "thumbsup", withExtension: "txt"),
Bundle.main.url(forResource: "thumbsdown", withExtension: "md"),
Bundle.main.url(forResource: "thumbsdown", withExtension: "html"),
Bundle.main.url(forResource: "greenthumb", withExtension: "thumb")
]
.compactMap { $0 }
.map { Document(url: $0) }
}
// MARK: - QLThumbnailGenerator
extension Document {
func generateThumbnail(
size: CGSize,
scale: CGFloat,
completion: @escaping (UIImage) -> Void
) {
let request = QLThumbnailGenerator.Request(
fileAt: url,
size: size,
scale: scale,
representationTypes: .all
)
let generator = QLThumbnailGenerator.shared
generator.generateRepresentations(for: request) { thumbnail, _, error in
if let thumbnail = thumbnail {
print("\(name) thumbnail generated")
completion(thumbnail.uiImage)
} else if let error = error {
print("\(name) - \(error)")
}
}
}
}
4. DocumentThumbnailView.swift
import SwiftUI
struct DocumentThumbnailView: View {
let document: Document
var thumbnailSize = CGSize(width: 150, height: 150)
@State var thumbnail = Image(systemName: "doc")
@Environment(\.displayScale) var displayScale: CGFloat
var body: some View {
GroupBox(label: Text(verbatim: document.name).font(.system(size: 12))) {
thumbnail
.font(.system(size: 120))
.foregroundColor(Color(.label))
.frame(width: thumbnailSize.width, height: thumbnailSize.height, alignment: .center)
.clipShape(RoundedRectangle(cornerRadius: 8, style: .continuous))
.padding()
}
.groupBoxStyle(PlainGroupBoxStyle())
.onAppear {
document.generateThumbnail(
size: thumbnailSize,
scale: displayScale
) { uiImage in
DispatchQueue.main.async {
self.thumbnail = Image(uiImage: uiImage)
}
}
}
}
}
struct PlainGroupBoxStyle: GroupBoxStyle {
func makeBody(configuration: Configuration) -> some View {
VStack(alignment: .center) {
configuration.label
.padding()
configuration.content
}
.background(Color(.systemGroupedBackground))
.clipShape(RoundedRectangle(cornerRadius: 8, style: .continuous))
}
}
5. DocumentPreviewView.swift
import SwiftUI
import QuickLook
struct DocumentPreviewView: View {
let document: Document
var body: some View {
QLPreviewView(previewItem: document.url as QLPreviewItem)
.navigationBarTitle(document.name, displayMode: .inline)
}
}
6. ThumbFile.swift
import Foundation
import UIKit.UIImage
struct ThumbFile: Codable {
let title: String
let imageBase64: String
init?(from fileURL: URL) {
guard
let data = FileManager.default.contents(atPath: fileURL.path),
let thumb = try? JSONDecoder().decode(Self.self, from: data)
else {
return nil
}
self = thumb
}
var uiImage: UIImage? {
return Data(base64Encoded: imageBase64)
.flatMap { UIImage(data: $0) }
}
}
7. ThumbFileViewController.swift
import UIKit
class ThumbFileViewController: UIViewController {
private lazy var titleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textAlignment = .center
label.font = UIFont.preferredFont(forTextStyle: .title1)
label.text = title
return label
}()
private lazy var imageView: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
view.addSubview(titleLabel)
view.addSubview(imageView)
NSLayoutConstraint.activate([
titleLabel.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor, constant: 30),
titleLabel.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor),
titleLabel.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor),
imageView.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 50.0),
imageView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
])
}
var thumbFile: ThumbFile? {
didSet {
titleLabel.text = thumbFile?.title
imageView.image = thumbFile?.uiImage
}
}
static func generateThumbnail(for thumbFile: ThumbFile, size: CGSize) -> UIImage {
dispatchPrecondition(condition: .onQueue(.main))
let viewController = ThumbFileViewController()
viewController.thumbFile = thumbFile
viewController.view.frame = CGRect(origin: .zero, size: CGSize(width: 450, height: 450))
viewController.view.updateConstraintsIfNeeded()
viewController.view.layoutIfNeeded()
let renderer = UIGraphicsImageRenderer(bounds: viewController.view.bounds)
return renderer.image { context in
viewController.view.layer.render(in: context.cgContext)
}
}
}
8. QLPreviewView.swift
import SwiftUI
import QuickLook
struct QLPreviewView: UIViewControllerRepresentable {
var previewItem: QLPreviewItem
func makeCoordinator() -> Coordinator {
Coordinator(parent: self)
}
func updateUIViewController(
_ viewController: QLPreviewController,
context: UIViewControllerRepresentableContext<QLPreviewView>
) {
viewController.reloadData()
}
func makeUIViewController(context: Context) -> QLPreviewController {
// For more on using QLPreviewController, check out this tutorial:
// https://www.raywenderlich.com/10447506-quicklook-previews-for-ios-getting-started
let controller = QLPreviewController()
controller.dataSource = context.coordinator
controller.reloadData()
return controller
}
class Coordinator: NSObject, QLPreviewControllerDataSource {
var parent: QLPreviewView
init(parent: QLPreviewView) {
self.parent = parent
super.init()
}
// MARK: - QLPreviewControllerDataSource
func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
return 1
}
func previewController(
_ controller: QLPreviewController,
previewItemAt index: Int
) -> QLPreviewItem {
return parent.previewItem
}
}
}
9. PreviewViewController.swift
import UIKit
import QuickLook
class PreviewViewController: ThumbFileViewController, QLPreviewingController {
enum ThumbFilePreviewError: Error {
case unableToOpenFile(atURL: URL)
}
func preparePreviewOfFile(
at url: URL,
completionHandler handler: @escaping (Error?) -> Void
) {
guard let thumbFile = ThumbFile(from: url) else {
handler(ThumbFilePreviewError.unableToOpenFile(atURL: url))
return
}
self.thumbFile = thumbFile
handler(nil)
}
}
10. ThumbnailProvider.swift
import UIKit
import QuickLookThumbnailing
class ThumbnailProvider: QLThumbnailProvider {
enum ThumbFileThumbnailError: Error {
case unableToOpenFile(atURL: URL)
}
override func provideThumbnail(
for request: QLFileThumbnailRequest,
_ handler: @escaping (QLThumbnailReply?, Error?) -> Void
) {
guard let thumbFile = ThumbFile(from: request.fileURL) else {
handler(nil, ThumbFileThumbnailError.unableToOpenFile(atURL: request.fileURL))
return
}
DispatchQueue.main.async {
let image = ThumbFileViewController.generateThumbnail(
for: thumbFile,
size: request.maximumSize
)
let reply = QLThumbnailReply(contextSize: request.maximumSize) {
image.draw(in: CGRect(origin: .zero, size: request.maximumSize))
return true
}
handler(reply, nil)
}
}
}
后记
本篇主要讲述了
QuickLook
预览和缩略图扩展的实现,感兴趣的给个赞或者关注~~~