Merge pull request #657 from mpretty-cyro/fix/use-yyimageview-only-when-needed
Only use YYImage for Gif/WebP images
This commit is contained in:
commit
b53ad0c5ed
|
@ -240,7 +240,7 @@ public final class FullConversationCell: UITableViewCell {
|
||||||
profile: cellViewModel.profile,
|
profile: cellViewModel.profile,
|
||||||
additionalProfile: cellViewModel.additionalProfile,
|
additionalProfile: cellViewModel.additionalProfile,
|
||||||
threadVariant: cellViewModel.threadVariant,
|
threadVariant: cellViewModel.threadVariant,
|
||||||
openGroupProfilePicture: cellViewModel.openGroupProfilePictureData.map { UIImage(data: $0) },
|
openGroupProfilePictureData: cellViewModel.openGroupProfilePictureData,
|
||||||
useFallbackPicture: (cellViewModel.threadVariant == .openGroup && cellViewModel.openGroupProfilePictureData == nil)
|
useFallbackPicture: (cellViewModel.threadVariant == .openGroup && cellViewModel.openGroupProfilePictureData == nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -280,7 +280,7 @@ public final class FullConversationCell: UITableViewCell {
|
||||||
profile: cellViewModel.profile,
|
profile: cellViewModel.profile,
|
||||||
additionalProfile: cellViewModel.additionalProfile,
|
additionalProfile: cellViewModel.additionalProfile,
|
||||||
threadVariant: cellViewModel.threadVariant,
|
threadVariant: cellViewModel.threadVariant,
|
||||||
openGroupProfilePicture: cellViewModel.openGroupProfilePictureData.map { UIImage(data: $0) },
|
openGroupProfilePictureData: cellViewModel.openGroupProfilePictureData,
|
||||||
useFallbackPicture: (cellViewModel.threadVariant == .openGroup && cellViewModel.openGroupProfilePictureData == nil)
|
useFallbackPicture: (cellViewModel.threadVariant == .openGroup && cellViewModel.openGroupProfilePictureData == nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -341,7 +341,7 @@ public final class FullConversationCell: UITableViewCell {
|
||||||
profile: cellViewModel.profile,
|
profile: cellViewModel.profile,
|
||||||
additionalProfile: cellViewModel.additionalProfile,
|
additionalProfile: cellViewModel.additionalProfile,
|
||||||
threadVariant: cellViewModel.threadVariant,
|
threadVariant: cellViewModel.threadVariant,
|
||||||
openGroupProfilePicture: cellViewModel.openGroupProfilePictureData.map { UIImage(data: $0) },
|
openGroupProfilePictureData: cellViewModel.openGroupProfilePictureData,
|
||||||
useFallbackPicture: (
|
useFallbackPicture: (
|
||||||
cellViewModel.threadVariant == .openGroup &&
|
cellViewModel.threadVariant == .openGroup &&
|
||||||
cellViewModel.openGroupProfilePictureData == nil
|
cellViewModel.openGroupProfilePictureData == nil
|
||||||
|
|
|
@ -94,7 +94,7 @@ final class SimplifiedConversationCell: UITableViewCell {
|
||||||
profile: cellViewModel.profile,
|
profile: cellViewModel.profile,
|
||||||
additionalProfile: cellViewModel.additionalProfile,
|
additionalProfile: cellViewModel.additionalProfile,
|
||||||
threadVariant: cellViewModel.threadVariant,
|
threadVariant: cellViewModel.threadVariant,
|
||||||
openGroupProfilePicture: cellViewModel.openGroupProfilePictureData.map { UIImage(data: $0) },
|
openGroupProfilePictureData: cellViewModel.openGroupProfilePictureData,
|
||||||
useFallbackPicture: (cellViewModel.threadVariant == .openGroup && cellViewModel.openGroupProfilePictureData == nil),
|
useFallbackPicture: (cellViewModel.threadVariant == .openGroup && cellViewModel.openGroupProfilePictureData == nil),
|
||||||
showMultiAvatarForClosedGroup: true
|
showMultiAvatarForClosedGroup: true
|
||||||
)
|
)
|
||||||
|
|
|
@ -34,6 +34,8 @@ public extension Data {
|
||||||
case (0x42, 0x4d): return .bmp
|
case (0x42, 0x4d): return .bmp
|
||||||
case (0x4D, 0x4D): return .tiff // Motorola byte order TIFF
|
case (0x4D, 0x4D): return .tiff // Motorola byte order TIFF
|
||||||
case (0x49, 0x49): return .tiff // Intel byte order TIFF
|
case (0x49, 0x49): return .tiff // Intel byte order TIFF
|
||||||
|
case (0x52, 0x49): return .webp // First two letters of WebP
|
||||||
|
|
||||||
default: return .unknown
|
default: return .unknown
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,6 +115,9 @@ public extension Data {
|
||||||
mimeType == OWSMimeTypeImageBmp1 ||
|
mimeType == OWSMimeTypeImageBmp1 ||
|
||||||
mimeType == OWSMimeTypeImageBmp2
|
mimeType == OWSMimeTypeImageBmp2
|
||||||
)
|
)
|
||||||
|
|
||||||
|
case .webp:
|
||||||
|
return (mimeType == nil || mimeType == OWSMimeTypeImageWebp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,4 +9,5 @@ public enum ImageFormat {
|
||||||
case tiff
|
case tiff
|
||||||
case jpeg
|
case jpeg
|
||||||
case bmp
|
case bmp
|
||||||
|
case webp
|
||||||
}
|
}
|
||||||
|
|
|
@ -328,7 +328,7 @@ typedef struct {
|
||||||
// Intel byte order TIFF
|
// Intel byte order TIFF
|
||||||
return ImageFormat_Tiff;
|
return ImageFormat_Tiff;
|
||||||
} else if (byte0 == 0x52 && byte1 == 0x49) {
|
} else if (byte0 == 0x52 && byte1 == 0x49) {
|
||||||
// First two letters of RIFF tag.
|
// First two letters of WebP tag.
|
||||||
return ImageFormat_Webp;
|
return ImageFormat_Webp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,8 +19,61 @@ public final class ProfilePictureView: UIView {
|
||||||
|
|
||||||
// MARK: - Components
|
// MARK: - Components
|
||||||
|
|
||||||
private lazy var imageView = getImageView()
|
private lazy var imageContainerView: UIView = {
|
||||||
private lazy var additionalImageView = getImageView()
|
let result: UIView = UIView()
|
||||||
|
result.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
result.clipsToBounds = true
|
||||||
|
result.backgroundColor = Colors.unimportant
|
||||||
|
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var imageView: UIImageView = {
|
||||||
|
let result: UIImageView = UIImageView()
|
||||||
|
result.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
result.contentMode = .scaleAspectFill
|
||||||
|
result.isHidden = true
|
||||||
|
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var animatedImageView: YYAnimatedImageView = {
|
||||||
|
let result: YYAnimatedImageView = YYAnimatedImageView()
|
||||||
|
result.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
result.contentMode = .scaleAspectFill
|
||||||
|
result.isHidden = true
|
||||||
|
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var additionalImageContainerView: UIView = {
|
||||||
|
let result: UIView = UIView()
|
||||||
|
result.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
result.clipsToBounds = true
|
||||||
|
result.backgroundColor = Colors.unimportant
|
||||||
|
result.layer.cornerRadius = (Values.smallProfilePictureSize / 2)
|
||||||
|
result.isHidden = true
|
||||||
|
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var additionalImageView: UIImageView = {
|
||||||
|
let result: UIImageView = UIImageView()
|
||||||
|
result.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
result.contentMode = .scaleAspectFill
|
||||||
|
result.isHidden = true
|
||||||
|
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var additionalAnimatedImageView: YYAnimatedImageView = {
|
||||||
|
let result: YYAnimatedImageView = YYAnimatedImageView()
|
||||||
|
result.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
result.contentMode = .scaleAspectFill
|
||||||
|
result.isHidden = true
|
||||||
|
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
// MARK: - Lifecycle
|
// MARK: - Lifecycle
|
||||||
|
|
||||||
|
@ -35,27 +88,33 @@ public final class ProfilePictureView: UIView {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setUpViewHierarchy() {
|
private func setUpViewHierarchy() {
|
||||||
// Set up image view
|
|
||||||
addSubview(imageView)
|
|
||||||
imageView.pin(.leading, to: .leading, of: self)
|
|
||||||
imageView.pin(.top, to: .top, of: self)
|
|
||||||
|
|
||||||
let imageViewSize = CGFloat(Values.mediumProfilePictureSize)
|
let imageViewSize = CGFloat(Values.mediumProfilePictureSize)
|
||||||
imageViewWidthConstraint = imageView.set(.width, to: imageViewSize)
|
|
||||||
imageViewHeightConstraint = imageView.set(.height, to: imageViewSize)
|
|
||||||
|
|
||||||
// Set up additional image view
|
|
||||||
addSubview(additionalImageView)
|
|
||||||
additionalImageView.pin(.trailing, to: .trailing, of: self)
|
|
||||||
additionalImageView.pin(.bottom, to: .bottom, of: self)
|
|
||||||
|
|
||||||
let additionalImageViewSize = CGFloat(Values.smallProfilePictureSize)
|
let additionalImageViewSize = CGFloat(Values.smallProfilePictureSize)
|
||||||
additionalImageViewWidthConstraint = additionalImageView.set(.width, to: additionalImageViewSize)
|
|
||||||
additionalImageViewHeightConstraint = additionalImageView.set(.height, to: additionalImageViewSize)
|
addSubview(imageContainerView)
|
||||||
additionalImageView.layer.cornerRadius = additionalImageViewSize / 2
|
addSubview(additionalImageContainerView)
|
||||||
|
|
||||||
|
imageContainerView.pin(.leading, to: .leading, of: self)
|
||||||
|
imageContainerView.pin(.top, to: .top, of: self)
|
||||||
|
imageViewWidthConstraint = imageContainerView.set(.width, to: imageViewSize)
|
||||||
|
imageViewHeightConstraint = imageContainerView.set(.height, to: imageViewSize)
|
||||||
|
additionalImageContainerView.pin(.trailing, to: .trailing, of: self)
|
||||||
|
additionalImageContainerView.pin(.bottom, to: .bottom, of: self)
|
||||||
|
additionalImageViewWidthConstraint = additionalImageContainerView.set(.width, to: additionalImageViewSize)
|
||||||
|
additionalImageViewHeightConstraint = additionalImageContainerView.set(.height, to: additionalImageViewSize)
|
||||||
|
|
||||||
|
imageContainerView.addSubview(imageView)
|
||||||
|
imageContainerView.addSubview(animatedImageView)
|
||||||
|
additionalImageContainerView.addSubview(additionalImageView)
|
||||||
|
additionalImageContainerView.addSubview(additionalAnimatedImageView)
|
||||||
|
|
||||||
|
imageView.pin(to: imageContainerView)
|
||||||
|
animatedImageView.pin(to: imageContainerView)
|
||||||
|
additionalImageView.pin(to: additionalImageContainerView)
|
||||||
|
additionalAnimatedImageView.pin(to: additionalImageContainerView)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Remove this once we refactor the ConversationVC to Swift (use the HomeViewModel approach)
|
// FIXME: Remove this once we refactor the OWSConversationSettingsViewController to Swift (use the HomeViewModel approach)
|
||||||
@objc(updateForThreadId:)
|
@objc(updateForThreadId:)
|
||||||
public func update(forThreadId threadId: String?) {
|
public func update(forThreadId threadId: String?) {
|
||||||
guard
|
guard
|
||||||
|
@ -74,7 +133,7 @@ public final class ProfilePictureView: UIView {
|
||||||
profile: viewModel.profile,
|
profile: viewModel.profile,
|
||||||
additionalProfile: viewModel.additionalProfile,
|
additionalProfile: viewModel.additionalProfile,
|
||||||
threadVariant: viewModel.threadVariant,
|
threadVariant: viewModel.threadVariant,
|
||||||
openGroupProfilePicture: viewModel.openGroupProfilePictureData.map { UIImage(data: $0) },
|
openGroupProfilePictureData: viewModel.openGroupProfilePictureData,
|
||||||
useFallbackPicture: (
|
useFallbackPicture: (
|
||||||
viewModel.threadVariant == .openGroup &&
|
viewModel.threadVariant == .openGroup &&
|
||||||
viewModel.openGroupProfilePictureData == nil
|
viewModel.openGroupProfilePictureData == nil
|
||||||
|
@ -88,7 +147,7 @@ public final class ProfilePictureView: UIView {
|
||||||
profile: Profile? = nil,
|
profile: Profile? = nil,
|
||||||
additionalProfile: Profile? = nil,
|
additionalProfile: Profile? = nil,
|
||||||
threadVariant: SessionThread.Variant,
|
threadVariant: SessionThread.Variant,
|
||||||
openGroupProfilePicture: UIImage? = nil,
|
openGroupProfilePictureData: Data? = nil,
|
||||||
useFallbackPicture: Bool = false,
|
useFallbackPicture: Bool = false,
|
||||||
showMultiAvatarForClosedGroup: Bool = false
|
showMultiAvatarForClosedGroup: Bool = false
|
||||||
) {
|
) {
|
||||||
|
@ -101,20 +160,38 @@ public final class ProfilePictureView: UIView {
|
||||||
}
|
}
|
||||||
|
|
||||||
imageView.contentMode = .center
|
imageView.contentMode = .center
|
||||||
imageView.backgroundColor = UIColor(rgbHex: 0x353535)
|
imageView.isHidden = false
|
||||||
imageView.layer.cornerRadius = (self.size / 2)
|
animatedImageView.isHidden = true
|
||||||
|
imageContainerView.backgroundColor = UIColor(rgbHex: 0x353535)
|
||||||
|
imageContainerView.layer.cornerRadius = (self.size / 2)
|
||||||
imageViewWidthConstraint.constant = self.size
|
imageViewWidthConstraint.constant = self.size
|
||||||
imageViewHeightConstraint.constant = self.size
|
imageViewHeightConstraint.constant = self.size
|
||||||
additionalImageView.isHidden = true
|
additionalImageContainerView.isHidden = true
|
||||||
|
animatedImageView.image = nil
|
||||||
additionalImageView.image = nil
|
additionalImageView.image = nil
|
||||||
additionalImageView.layer.cornerRadius = (self.size / 2)
|
additionalAnimatedImageView.image = nil
|
||||||
|
additionalImageView.isHidden = true
|
||||||
|
additionalAnimatedImageView.isHidden = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard !publicKey.isEmpty || openGroupProfilePicture != nil else { return }
|
guard !publicKey.isEmpty || openGroupProfilePictureData != nil else { return }
|
||||||
|
|
||||||
func getProfilePicture(of size: CGFloat, for publicKey: String, profile: Profile?) -> (image: UIImage, isTappable: Bool) {
|
func getProfilePicture(of size: CGFloat, for publicKey: String, profile: Profile?) -> (image: UIImage?, animatedImage: YYImage?, isTappable: Bool) {
|
||||||
if let profile: Profile = profile, let profileData: Data = ProfileManager.profileAvatar(profile: profile), let image: YYImage = YYImage(data: profileData) {
|
if let profile: Profile = profile, let profileData: Data = ProfileManager.profileAvatar(profile: profile) {
|
||||||
return (image, true)
|
let format: ImageFormat = profileData.guessedImageFormat
|
||||||
|
|
||||||
|
let image: UIImage? = (format == .gif || format == .webp ?
|
||||||
|
nil :
|
||||||
|
UIImage(data: profileData)
|
||||||
|
)
|
||||||
|
let animatedImage: YYImage? = (format != .gif && format != .webp ?
|
||||||
|
nil :
|
||||||
|
YYImage(data: profileData)
|
||||||
|
)
|
||||||
|
|
||||||
|
if image != nil || animatedImage != nil {
|
||||||
|
return (image, animatedImage, true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -124,6 +201,7 @@ public final class ProfilePictureView: UIView {
|
||||||
.defaulting(to: publicKey),
|
.defaulting(to: publicKey),
|
||||||
size: size
|
size: size
|
||||||
),
|
),
|
||||||
|
nil,
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -147,56 +225,75 @@ public final class ProfilePictureView: UIView {
|
||||||
imageViewHeightConstraint.constant = targetSize
|
imageViewHeightConstraint.constant = targetSize
|
||||||
additionalImageViewWidthConstraint.constant = targetSize
|
additionalImageViewWidthConstraint.constant = targetSize
|
||||||
additionalImageViewHeightConstraint.constant = targetSize
|
additionalImageViewHeightConstraint.constant = targetSize
|
||||||
additionalImageView.isHidden = false
|
additionalImageContainerView.isHidden = false
|
||||||
|
|
||||||
if let additionalProfile: Profile = additionalProfile {
|
if let additionalProfile: Profile = additionalProfile {
|
||||||
additionalImageView.image = getProfilePicture(
|
let (image, animatedImage, _): (UIImage?, YYImage?, Bool) = getProfilePicture(
|
||||||
of: targetSize,
|
of: targetSize,
|
||||||
for: additionalProfile.id,
|
for: additionalProfile.id,
|
||||||
profile: additionalProfile
|
profile: additionalProfile
|
||||||
).image
|
)
|
||||||
|
|
||||||
|
// Set the images and show the appropriate imageView (non-animated should be
|
||||||
|
// visible if there is no image)
|
||||||
|
additionalImageView.image = image
|
||||||
|
additionalAnimatedImageView.image = animatedImage
|
||||||
|
additionalImageView.isHidden = (animatedImage != nil)
|
||||||
|
additionalAnimatedImageView.isHidden = (animatedImage == nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
targetSize = self.size
|
targetSize = self.size
|
||||||
imageViewWidthConstraint.constant = targetSize
|
imageViewWidthConstraint.constant = targetSize
|
||||||
imageViewHeightConstraint.constant = targetSize
|
imageViewHeightConstraint.constant = targetSize
|
||||||
additionalImageView.isHidden = true
|
additionalImageContainerView.isHidden = true
|
||||||
additionalImageView.image = nil
|
additionalImageView.image = nil
|
||||||
|
additionalImageView.isHidden = true
|
||||||
|
additionalAnimatedImageView.image = nil
|
||||||
|
additionalAnimatedImageView.isHidden = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the image
|
// Set the image
|
||||||
if let openGroupProfilePicture: UIImage = openGroupProfilePicture {
|
if let openGroupProfilePictureData: Data = openGroupProfilePictureData {
|
||||||
imageView.image = openGroupProfilePicture
|
let format: ImageFormat = openGroupProfilePictureData.guessedImageFormat
|
||||||
|
|
||||||
|
let image: UIImage? = (format == .gif || format == .webp ?
|
||||||
|
nil :
|
||||||
|
UIImage(data: openGroupProfilePictureData)
|
||||||
|
)
|
||||||
|
let animatedImage: YYImage? = (format != .gif && format != .webp ?
|
||||||
|
nil :
|
||||||
|
YYImage(data: openGroupProfilePictureData)
|
||||||
|
)
|
||||||
|
|
||||||
|
imageView.image = image
|
||||||
|
animatedImageView.image = animatedImage
|
||||||
|
imageView.isHidden = (animatedImage != nil)
|
||||||
|
animatedImageView.isHidden = (animatedImage == nil)
|
||||||
hasTappableProfilePicture = true
|
hasTappableProfilePicture = true
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let (image, isTappable): (UIImage, Bool) = getProfilePicture(
|
let (image, animatedImage, isTappable): (UIImage?, YYImage?, Bool) = getProfilePicture(
|
||||||
of: targetSize,
|
of: targetSize,
|
||||||
for: publicKey,
|
for: publicKey,
|
||||||
profile: profile
|
profile: profile
|
||||||
)
|
)
|
||||||
imageView.image = image
|
imageView.image = image
|
||||||
|
animatedImageView.image = animatedImage
|
||||||
|
imageView.isHidden = (animatedImage != nil)
|
||||||
|
animatedImageView.isHidden = (animatedImage == nil)
|
||||||
hasTappableProfilePicture = isTappable
|
hasTappableProfilePicture = isTappable
|
||||||
}
|
}
|
||||||
|
|
||||||
imageView.contentMode = .scaleAspectFill
|
imageView.contentMode = .scaleAspectFill
|
||||||
imageView.backgroundColor = Colors.unimportant
|
animatedImageView.contentMode = .scaleAspectFill
|
||||||
imageView.layer.cornerRadius = (targetSize / 2)
|
imageContainerView.backgroundColor = Colors.unimportant
|
||||||
additionalImageView.layer.cornerRadius = (targetSize / 2)
|
imageContainerView.layer.cornerRadius = (targetSize / 2)
|
||||||
|
additionalImageContainerView.layer.cornerRadius = (targetSize / 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Convenience
|
// MARK: - Convenience
|
||||||
|
|
||||||
private func getImageView() -> YYAnimatedImageView {
|
|
||||||
let result = YYAnimatedImageView()
|
|
||||||
result.layer.masksToBounds = true
|
|
||||||
result.backgroundColor = Colors.unimportant
|
|
||||||
result.contentMode = .scaleAspectFill
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc public func getProfilePicture() -> UIImage? {
|
@objc public func getProfilePicture() -> UIImage? {
|
||||||
return (hasTappableProfilePicture ? imageView.image : nil)
|
return (hasTappableProfilePicture ? imageView.image : nil)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue