Merge branch 'fix/profile-timeline' into develop

This commit is contained in:
CMK 2021-06-17 13:45:24 +08:00
commit 7b52da8757
4 changed files with 71 additions and 61 deletions

View File

@ -251,7 +251,7 @@ extension StatusSection {
default: return 0.7
}
}()
return CGSize(width: maxWidth, height: maxWidth * scale)
return CGSize(width: maxWidth, height: floor(maxWidth * scale))
}()
let mosaics: [MosaicImageViewContainer.ConfigurableMosaic] = {
if mosaicImageViewModel.metas.count == 1 {
@ -268,13 +268,16 @@ extension StatusSection {
let blurhashOverlayImageView = mosaic.blurhashOverlayImageView
let meta = mosaicImageViewModel.metas[i]
// set blurhash image
meta.blurhashImagePublisher()
.receive(on: RunLoop.main)
.sink { image in
blurhashOverlayImageView.image = image
}
.store(in: &cell.disposeBag)
let isSingleMosaicLayout = mosaics.count == 1
// set image
let imageSize = CGSize(
width: mosaic.imageViewSize.width * imageView.traitCollection.displayScale,
height: mosaic.imageViewSize.height * imageView.traitCollection.displayScale
@ -282,12 +285,18 @@ extension StatusSection {
let request = ImageRequest(
url: meta.url,
processors: [
ImageProcessors.Resize(size: imageSize, contentMode: .aspectFill)
ImageProcessors.Resize(
size: imageSize,
unit: .pixels,
contentMode: isSingleMosaicLayout ? .aspectFill : .aspectFit,
crop: isSingleMosaicLayout
)
]
)
let options = ImageLoadingOptions(
transition: .fadeIn(duration: 0.2)
)
Nuke.loadImage(
with: request,
options: options,
@ -296,28 +305,17 @@ extension StatusSection {
switch result {
case .failure:
break
case .success:
case .success(let response)
statusItemAttribute.isImageLoaded.value = true
}
}
//imageView.af.setImage(
// withURL: meta.url,
// placeholderImage: UIImage.placeholder(color: .systemFill),
// imageTransition: .crossDissolve(0.2)
//) { response in
// switch response.result {
// case .success:
// statusItemAttribute.isImageLoaded.value = true
// case .failure:
// break
// }
//}
imageView.accessibilityLabel = meta.altText
Publishers.CombineLatest(
statusItemAttribute.isImageLoaded,
statusItemAttribute.isRevealing
)
.receive(on: RunLoop.main)
.receive(on: DispatchQueue.main) // needs call immediately
.sink { [weak cell] isImageLoaded, isMediaRevealing in
guard let cell = cell else { return }
guard isImageLoaded else {

View File

@ -104,7 +104,7 @@ extension MosaicImageViewContainer {
imageViews = []
blurhashOverlayImageViews = []
container.spacing = 1
container.spacing = UIView.separatorLineHeight(of: self) * 2 // 2px
}
struct ConfigurableMosaic {
@ -120,12 +120,16 @@ extension MosaicImageViewContainer {
contentView.translatesAutoresizingMaskIntoConstraints = false
container.addArrangedSubview(contentView)
let rect = AVMakeRect(
aspectRatio: aspectRatio,
insideRect: CGRect(origin: .zero, size: maxSize)
)
let imageViewSize: CGSize = {
let rect = AVMakeRect(
aspectRatio: aspectRatio,
insideRect: CGRect(origin: .zero, size: maxSize)
).integral
return rect.size
}()
let imageViewFrame = CGRect(origin: .zero, size: imageViewSize)
let imageView = UIImageView()
let imageView = UIImageView(frame: imageViewFrame)
imageViews.append(imageView)
imageView.layer.masksToBounds = true
imageView.layer.cornerRadius = ContentWarningOverlayView.cornerRadius
@ -138,9 +142,9 @@ extension MosaicImageViewContainer {
imageView.topAnchor.constraint(equalTo: contentView.topAnchor),
imageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
imageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
imageView.widthAnchor.constraint(equalToConstant: floor(rect.width)).priority(.required - 1),
imageView.widthAnchor.constraint(equalToConstant: imageViewFrame.width).priority(.required - 1),
])
containerHeightLayoutConstraint.constant = floor(rect.height)
containerHeightLayoutConstraint.constant = imageViewFrame.height
containerHeightLayoutConstraint.isActive = true
let blurhashOverlayImageView = UIImageView()
@ -170,7 +174,7 @@ extension MosaicImageViewContainer {
return ConfigurableMosaic(
imageView: imageView,
blurhashOverlayImageView: blurhashOverlayImageView,
imageViewSize: maxSize
imageViewSize: imageViewSize
)
}
@ -181,6 +185,7 @@ extension MosaicImageViewContainer {
}
let maxHeight = maxSize.height
let spacing: CGFloat = 1
containerHeightLayoutConstraint.constant = maxHeight
containerHeightLayoutConstraint.isActive = true
@ -190,7 +195,7 @@ extension MosaicImageViewContainer {
[contentLeftStackView, contentRightStackView].forEach { stackView in
stackView.axis = .vertical
stackView.distribution = .fillEqually
stackView.spacing = 1
stackView.spacing = spacing
}
container.addArrangedSubview(contentLeftStackView)
container.addArrangedSubview(contentRightStackView)
@ -310,22 +315,23 @@ extension MosaicImageViewContainer {
let imageViewSize: CGSize = {
switch (i, count) {
case (_, 4):
return CGSize(width: maxSize.width * 0.5, height: maxSize.height * 0.5)
return CGSize(width: maxSize.width * 0.5 - spacing, height: maxSize.height * 0.5 - spacing)
case (i, 3):
let width = maxSize.width * 0.5
let width = maxSize.width * 0.5 - spacing
if i == 0 {
return CGSize(width: width, height: maxSize.height)
} else {
return CGSize(width: width, height: maxSize.height * 0.5)
return CGSize(width: width, height: maxSize.height * 0.5 - spacing)
}
case (_, 2):
let width = maxSize.width * 0.5
let width = maxSize.width * 0.5 - spacing
return CGSize(width: width, height: maxSize.height)
default:
assertionFailure()
return maxSize
}
}()
imageView.frame.size = imageViewSize
let mosaic = ConfigurableMosaic(
imageView: imageView,
blurhashOverlayImageView: blurhashOverlayImageView,

View File

@ -15,22 +15,25 @@ final class BlurhashImageCacheService {
let workingQueue = DispatchQueue(label: "org.joinmastodon.app.BlurhashImageCacheService.working-queue", qos: .userInitiated, attributes: .concurrent)
func image(blurhash: String, size: CGSize, url: URL) -> AnyPublisher<UIImage?, Never> {
let key = Key(blurhash: blurhash, size: size, url: url)
if let image = self.cache.object(forKey: key) {
return Just(image).eraseToAnyPublisher()
}
return Future { promise in
self.workingQueue.async {
let key = Key(blurhash: blurhash, size: size, url: url)
guard let image = self.cache.object(forKey: key) else {
if let image = BlurhashImageCacheService.blurhashImage(blurhash: blurhash, size: size, url: url) {
self.cache.setObject(image, forKey: key)
promise(.success(image))
} else {
promise(.success(nil))
}
guard let image = BlurhashImageCacheService.blurhashImage(blurhash: blurhash, size: size, url: url) else {
promise(.success(nil))
return
}
self.cache.setObject(image, forKey: key)
promise(.success(image))
}
}
.receive(on: RunLoop.main)
.eraseToAnyPublisher()
}
static func blurhashImage(blurhash: String, size: CGSize, url: URL) -> UIImage? {
@ -55,13 +58,7 @@ final class BlurhashImageCacheService {
}
extension BlurhashImageCacheService {
class Key: Hashable {
static func == (lhs: BlurhashImageCacheService.Key, rhs: BlurhashImageCacheService.Key) -> Bool {
return lhs.blurhash == rhs.blurhash
&& lhs.size == rhs.size
&& lhs.url == rhs.url
}
class Key: NSObject {
let blurhash: String
let size: CGSize
let url: URL
@ -72,11 +69,19 @@ extension BlurhashImageCacheService {
self.url = url
}
func hash(into hasher: inout Hasher) {
hasher.combine(blurhash)
hasher.combine(size.width)
hasher.combine(size.height)
hasher.combine(url)
override func isEqual(_ object: Any?) -> Bool {
guard let object = object as? Key else { return false }
return object.blurhash == blurhash
&& object.size == size
&& object.url == url
}
override var hash: Int {
return blurhash.hashValue ^
size.width.hashValue ^
size.height.hashValue ^
url.hashValue
}
}
}

View File

@ -33,7 +33,7 @@ final class PlaceholderImageCacheService {
}
extension PlaceholderImageCacheService {
class Key: Hashable {
class Key: NSObject {
let color: UIColor
let size: CGSize
let cornerRadius: CGFloat
@ -44,17 +44,18 @@ extension PlaceholderImageCacheService {
self.cornerRadius = cornerRadius
}
static func == (lhs: PlaceholderImageCacheService.Key, rhs: PlaceholderImageCacheService.Key) -> Bool {
return lhs.color == rhs.color
&& lhs.size == rhs.size
&& lhs.cornerRadius == rhs.cornerRadius
override func isEqual(_ object: Any?) -> Bool {
guard let object = object as? Key else { return false }
return object.color == color
&& object.size == size
&& object.cornerRadius == cornerRadius
}
func hash(into hasher: inout Hasher) {
hasher.combine(color)
hasher.combine(size.width)
hasher.combine(size.height)
hasher.combine(cornerRadius)
override var hash: Int {
return color.hashValue ^
size.width.hashValue ^
size.height.hashValue ^
cornerRadius.hashValue
}
}
}