mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-15 05:37:32 +00:00
Done PhotoPicker
This commit is contained in:
@@ -11,10 +11,11 @@ private let attachmentSize: CGFloat = 100
|
||||
private let attachmentSpacing: CGFloat = 16
|
||||
|
||||
class AttachmentBannerView: UIScrollView {
|
||||
var attachments: [UIImage] = [] {
|
||||
didSet {
|
||||
rebuildViews()
|
||||
}
|
||||
var readAttachments: (() -> ([UIImage]))?
|
||||
var onAttachmentsDelete: ((Int) -> Void)?
|
||||
var attachments: [UIImage] {
|
||||
get { readAttachments?() ?? [] }
|
||||
set { assertionFailure() }
|
||||
}
|
||||
|
||||
override var intrinsicContentSize: CGSize {
|
||||
@@ -26,6 +27,8 @@ class AttachmentBannerView: UIScrollView {
|
||||
)
|
||||
}
|
||||
|
||||
let stackView = UIStackView()
|
||||
|
||||
init() {
|
||||
super.init(frame: .zero)
|
||||
|
||||
@@ -34,6 +37,19 @@ class AttachmentBannerView: UIScrollView {
|
||||
showsHorizontalScrollIndicator = false
|
||||
showsVerticalScrollIndicator = false
|
||||
|
||||
stackView.axis = .horizontal
|
||||
stackView.spacing = attachmentSpacing
|
||||
stackView.alignment = .center
|
||||
stackView.distribution = .fill
|
||||
stackView.translatesAutoresizingMaskIntoConstraints = false
|
||||
addSubview(stackView)
|
||||
[
|
||||
stackView.topAnchor.constraint(equalTo: topAnchor),
|
||||
stackView.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||
stackView.trailingAnchor.constraint(equalTo: trailingAnchor),
|
||||
stackView.bottomAnchor.constraint(equalTo: bottomAnchor),
|
||||
].forEach { $0.isActive = true }
|
||||
|
||||
rebuildViews()
|
||||
}
|
||||
|
||||
@@ -42,23 +58,47 @@ class AttachmentBannerView: UIScrollView {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
var reusableViews = [AttachmentPreviewView]()
|
||||
|
||||
func rebuildViews() {
|
||||
subviews.forEach { $0.removeFromSuperview() }
|
||||
for (index, attachment) in attachments.enumerated() {
|
||||
let view = AttachmentPreviewView()
|
||||
view.imageView.image = attachment
|
||||
addSubview(view)
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.frame = .init(
|
||||
origin: .init(
|
||||
x: (attachmentSize + attachmentSpacing) * CGFloat(index),
|
||||
y: 0
|
||||
),
|
||||
size: .init(width: attachmentSize, height: attachmentSize)
|
||||
)
|
||||
let attachments = attachments
|
||||
|
||||
if reusableViews.count > attachments.count {
|
||||
for index in attachments.count ..< reusableViews.count {
|
||||
reusableViews[index].removeFromSuperview()
|
||||
}
|
||||
reusableViews.removeLast(reusableViews.count - attachments.count)
|
||||
}
|
||||
if reusableViews.count < attachments.count {
|
||||
for _ in reusableViews.count ..< attachments.count {
|
||||
let view = AttachmentPreviewView()
|
||||
view.alpha = 0
|
||||
reusableViews.append(view)
|
||||
}
|
||||
}
|
||||
|
||||
assert(reusableViews.count == attachments.count)
|
||||
|
||||
for (index, attachment) in attachments.enumerated() {
|
||||
let view = reusableViews[index]
|
||||
view.imageView.image = attachment
|
||||
stackView.addArrangedSubview(view)
|
||||
view.deleteButtonAction = { [weak self] in
|
||||
self?.onAttachmentsDelete?(index)
|
||||
}
|
||||
}
|
||||
|
||||
invalidateIntrinsicContentSize()
|
||||
contentSize = intrinsicContentSize
|
||||
UIView.performWithoutAnimation {
|
||||
self.layoutIfNeeded()
|
||||
}
|
||||
|
||||
UIView.animate(withDuration: 0.3) {
|
||||
for view in self.reusableViews {
|
||||
view.alpha = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +107,8 @@ extension AttachmentBannerView {
|
||||
let imageView = UIImageView()
|
||||
let deleteButton = UIButton()
|
||||
|
||||
var deleteButtonAction: (() -> Void)?
|
||||
|
||||
override var intrinsicContentSize: CGSize {
|
||||
.init(width: attachmentSize, height: attachmentSize)
|
||||
}
|
||||
@@ -100,6 +142,8 @@ extension AttachmentBannerView {
|
||||
deleteButton.heightAnchor.constraint(equalToConstant: 32),
|
||||
].forEach { $0.isActive = true }
|
||||
|
||||
deleteButton.addTarget(self, action: #selector(deleteButtonTapped), for: .touchUpInside)
|
||||
|
||||
[
|
||||
widthAnchor.constraint(equalToConstant: attachmentSize),
|
||||
heightAnchor.constraint(equalToConstant: attachmentSize),
|
||||
@@ -110,5 +154,10 @@ extension AttachmentBannerView {
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
@objc func deleteButtonTapped() {
|
||||
deleteButtonAction?()
|
||||
deleteButtonAction = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
//
|
||||
// InputEditView+Camera.swift
|
||||
// Intelligents
|
||||
//
|
||||
// Created by 秋星桥 on 2024/12/6.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
extension InputEditView {
|
||||
@objc func takePhoto() {}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// InputEditView+Photo.swift
|
||||
// Intelligents
|
||||
//
|
||||
// Created by 秋星桥 on 2024/12/6.
|
||||
//
|
||||
|
||||
import PhotosUI
|
||||
import UIKit
|
||||
|
||||
extension InputEditView: PHPickerViewControllerDelegate {
|
||||
@objc func selectPhoto() {
|
||||
var config = PHPickerConfiguration(photoLibrary: .shared())
|
||||
config.filter = .images
|
||||
config.selectionLimit = 9
|
||||
let picker = PHPickerViewController(configuration: config)
|
||||
picker.modalPresentationStyle = .formSheet
|
||||
picker.delegate = self
|
||||
parentViewController?.present(picker, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
|
||||
picker.dismiss(animated: true)
|
||||
loadPNG(from: results)
|
||||
}
|
||||
|
||||
private func loadPNG(from results: [PHPickerResult]) {
|
||||
for result in results {
|
||||
result.itemProvider.loadObject(ofClass: UIImage.self) { [weak self] image, _ in
|
||||
if let image = image as? UIImage {
|
||||
DispatchQueue.main.async {
|
||||
self?.viewModel.attachments.append(image)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,6 +52,24 @@ class InputEditView: UIView, UITextViewDelegate {
|
||||
].forEach { $0.isActive = true }
|
||||
}
|
||||
|
||||
attachmentsEditor.readAttachments = { [weak self] in
|
||||
self?.viewModel.attachments ?? []
|
||||
}
|
||||
attachmentsEditor.onAttachmentsDelete = { [weak self] index in
|
||||
self?.viewModel.attachments.remove(at: index)
|
||||
}
|
||||
|
||||
controlBanner.cameraButton.addTarget(
|
||||
self,
|
||||
action: #selector(takePhoto),
|
||||
for: .touchUpInside
|
||||
)
|
||||
controlBanner.photoButton.addTarget(
|
||||
self,
|
||||
action: #selector(selectPhoto),
|
||||
for: .touchUpInside
|
||||
)
|
||||
|
||||
textEditor.addSubview(placeholderLabel)
|
||||
placeholderLabel.textColor = .label.withAlphaComponent(0.25)
|
||||
placeholderLabel.font = textEditor.font
|
||||
@@ -106,9 +124,7 @@ class InputEditView: UIView, UITextViewDelegate {
|
||||
if textEditor.text != viewModel.text {
|
||||
textEditor.text = viewModel.text
|
||||
}
|
||||
if attachmentsEditor.attachments != viewModel.attachments {
|
||||
attachmentsEditor.attachments = viewModel.attachments
|
||||
}
|
||||
attachmentsEditor.rebuildViews()
|
||||
parentViewController?.view.layoutIfNeeded()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user