mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-24 18:02:47 +08:00
Done PhotoPicker
This commit is contained in:
@@ -11,10 +11,11 @@ private let attachmentSize: CGFloat = 100
|
|||||||
private let attachmentSpacing: CGFloat = 16
|
private let attachmentSpacing: CGFloat = 16
|
||||||
|
|
||||||
class AttachmentBannerView: UIScrollView {
|
class AttachmentBannerView: UIScrollView {
|
||||||
var attachments: [UIImage] = [] {
|
var readAttachments: (() -> ([UIImage]))?
|
||||||
didSet {
|
var onAttachmentsDelete: ((Int) -> Void)?
|
||||||
rebuildViews()
|
var attachments: [UIImage] {
|
||||||
}
|
get { readAttachments?() ?? [] }
|
||||||
|
set { assertionFailure() }
|
||||||
}
|
}
|
||||||
|
|
||||||
override var intrinsicContentSize: CGSize {
|
override var intrinsicContentSize: CGSize {
|
||||||
@@ -26,6 +27,8 @@ class AttachmentBannerView: UIScrollView {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let stackView = UIStackView()
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
super.init(frame: .zero)
|
super.init(frame: .zero)
|
||||||
|
|
||||||
@@ -34,6 +37,19 @@ class AttachmentBannerView: UIScrollView {
|
|||||||
showsHorizontalScrollIndicator = false
|
showsHorizontalScrollIndicator = false
|
||||||
showsVerticalScrollIndicator = 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()
|
rebuildViews()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,23 +58,47 @@ class AttachmentBannerView: UIScrollView {
|
|||||||
fatalError()
|
fatalError()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var reusableViews = [AttachmentPreviewView]()
|
||||||
|
|
||||||
func rebuildViews() {
|
func rebuildViews() {
|
||||||
subviews.forEach { $0.removeFromSuperview() }
|
let attachments = attachments
|
||||||
for (index, attachment) in attachments.enumerated() {
|
|
||||||
let view = AttachmentPreviewView()
|
if reusableViews.count > attachments.count {
|
||||||
view.imageView.image = attachment
|
for index in attachments.count ..< reusableViews.count {
|
||||||
addSubview(view)
|
reusableViews[index].removeFromSuperview()
|
||||||
view.translatesAutoresizingMaskIntoConstraints = false
|
}
|
||||||
view.frame = .init(
|
reusableViews.removeLast(reusableViews.count - attachments.count)
|
||||||
origin: .init(
|
|
||||||
x: (attachmentSize + attachmentSpacing) * CGFloat(index),
|
|
||||||
y: 0
|
|
||||||
),
|
|
||||||
size: .init(width: attachmentSize, height: attachmentSize)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
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()
|
invalidateIntrinsicContentSize()
|
||||||
contentSize = intrinsicContentSize
|
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 imageView = UIImageView()
|
||||||
let deleteButton = UIButton()
|
let deleteButton = UIButton()
|
||||||
|
|
||||||
|
var deleteButtonAction: (() -> Void)?
|
||||||
|
|
||||||
override var intrinsicContentSize: CGSize {
|
override var intrinsicContentSize: CGSize {
|
||||||
.init(width: attachmentSize, height: attachmentSize)
|
.init(width: attachmentSize, height: attachmentSize)
|
||||||
}
|
}
|
||||||
@@ -100,6 +142,8 @@ extension AttachmentBannerView {
|
|||||||
deleteButton.heightAnchor.constraint(equalToConstant: 32),
|
deleteButton.heightAnchor.constraint(equalToConstant: 32),
|
||||||
].forEach { $0.isActive = true }
|
].forEach { $0.isActive = true }
|
||||||
|
|
||||||
|
deleteButton.addTarget(self, action: #selector(deleteButtonTapped), for: .touchUpInside)
|
||||||
|
|
||||||
[
|
[
|
||||||
widthAnchor.constraint(equalToConstant: attachmentSize),
|
widthAnchor.constraint(equalToConstant: attachmentSize),
|
||||||
heightAnchor.constraint(equalToConstant: attachmentSize),
|
heightAnchor.constraint(equalToConstant: attachmentSize),
|
||||||
@@ -110,5 +154,10 @@ extension AttachmentBannerView {
|
|||||||
required init?(coder _: NSCoder) {
|
required init?(coder _: NSCoder) {
|
||||||
fatalError()
|
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 }
|
].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)
|
textEditor.addSubview(placeholderLabel)
|
||||||
placeholderLabel.textColor = .label.withAlphaComponent(0.25)
|
placeholderLabel.textColor = .label.withAlphaComponent(0.25)
|
||||||
placeholderLabel.font = textEditor.font
|
placeholderLabel.font = textEditor.font
|
||||||
@@ -106,9 +124,7 @@ class InputEditView: UIView, UITextViewDelegate {
|
|||||||
if textEditor.text != viewModel.text {
|
if textEditor.text != viewModel.text {
|
||||||
textEditor.text = viewModel.text
|
textEditor.text = viewModel.text
|
||||||
}
|
}
|
||||||
if attachmentsEditor.attachments != viewModel.attachments {
|
attachmentsEditor.rebuildViews()
|
||||||
attachmentsEditor.attachments = viewModel.attachments
|
|
||||||
}
|
|
||||||
parentViewController?.view.layoutIfNeeded()
|
parentViewController?.view.layoutIfNeeded()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user