@ -7,14 +7,107 @@ import SessionUIKit
import SessionMessagingKit
import SessionMessagingKit
public final class ProfilePictureView : UIView {
public final class ProfilePictureView : UIView {
public enum Size {
case navigation
case message
case list
case hero
public var viewSize : CGFloat {
switch self {
case . navigation , . message : return 26
case . list : return 46
case . hero : return 110
}
}
var imageSize : CGFloat {
switch self {
case . navigation , . message : return 26
case . list : return 46
case . hero : return 80
}
}
var multiImageSize : CGFloat {
switch self {
case . navigation , . message : return 18 // S h o u l d n ' t b e u s e d
case . list : return 32
case . hero : return 80
}
}
var iconSize : CGFloat {
switch self {
case . navigation , . message : return 8
case . list : return 16
case . hero : return 24
}
}
var iconVerticalInset : CGFloat {
switch self {
case . navigation , . message : return 1
case . list : return 3
case . hero : return 5
}
}
}
public enum ProfileIcon {
case none
case crown
case rightPlus
}
public var size : Size {
didSet {
widthConstraint . constant = ( customWidth ? ? size . viewSize )
heightConstraint . constant = size . viewSize
profileIconTopConstraint . constant = size . iconVerticalInset
profileIconBottomConstraint . constant = - size . iconVerticalInset
profileIconBackgroundWidthConstraint . constant = size . iconSize
profileIconBackgroundHeightConstraint . constant = size . iconSize
additionalProfileIconTopConstraint . constant = size . iconVerticalInset
additionalProfileIconBottomConstraint . constant = - size . iconVerticalInset
additionalProfileIconBackgroundWidthConstraint . constant = size . iconSize
additionalProfileIconBackgroundHeightConstraint . constant = size . iconSize
profileIconBackgroundView . layer . cornerRadius = ( size . iconSize / 2 )
additionalProfileIconBackgroundView . layer . cornerRadius = ( size . iconSize / 2 )
}
}
public var customWidth : CGFloat ? {
didSet {
self . widthConstraint . constant = ( customWidth ? ? self . size . viewSize )
}
}
private var hasTappableProfilePicture : Bool = false
private var hasTappableProfilePicture : Bool = false
public var size : CGFloat = 0
// C o n s t r a i n t s
// MARK: - C o n s t r a i n t s
private var widthConstraint : NSLayoutConstraint !
private var heightConstraint : NSLayoutConstraint !
private var imageViewTopConstraint : NSLayoutConstraint !
private var imageViewLeadingConstraint : NSLayoutConstraint !
private var imageViewCenterXConstraint : NSLayoutConstraint !
private var imageViewCenterYConstraint : NSLayoutConstraint !
private var imageViewWidthConstraint : NSLayoutConstraint !
private var imageViewWidthConstraint : NSLayoutConstraint !
private var imageViewHeightConstraint : NSLayoutConstraint !
private var imageViewHeightConstraint : NSLayoutConstraint !
private var additionalImageViewWidthConstraint : NSLayoutConstraint !
private var additionalImageViewWidthConstraint : NSLayoutConstraint !
private var additionalImageViewHeightConstraint : NSLayoutConstraint !
private var additionalImageViewHeightConstraint : NSLayoutConstraint !
private var profileIconTopConstraint : NSLayoutConstraint !
private var profileIconBottomConstraint : NSLayoutConstraint !
private var profileIconBackgroundLeftAlignConstraint : NSLayoutConstraint !
private var profileIconBackgroundRightAlignConstraint : NSLayoutConstraint !
private var profileIconBackgroundWidthConstraint : NSLayoutConstraint !
private var profileIconBackgroundHeightConstraint : NSLayoutConstraint !
private var additionalProfileIconTopConstraint : NSLayoutConstraint !
private var additionalProfileIconBottomConstraint : NSLayoutConstraint !
private var additionalProfileIconBackgroundLeftAlignConstraint : NSLayoutConstraint !
private var additionalProfileIconBackgroundRightAlignConstraint : NSLayoutConstraint !
private var additionalProfileIconBackgroundWidthConstraint : NSLayoutConstraint !
private var additionalProfileIconBackgroundHeightConstraint : NSLayoutConstraint !
// MARK: - C o m p o n e n t s
// MARK: - C o m p o n e n t s
@ -51,7 +144,6 @@ public final class ProfilePictureView: UIView {
result . clipsToBounds = true
result . clipsToBounds = true
result . themeBackgroundColor = . primary
result . themeBackgroundColor = . primary
result . themeBorderColor = . backgroundPrimary
result . themeBorderColor = . backgroundPrimary
result . layer . cornerRadius = ( Values . smallProfilePictureSize / 2 )
result . isHidden = true
result . isHidden = true
return result
return result
@ -88,33 +180,72 @@ public final class ProfilePictureView: UIView {
return result
return result
} ( )
} ( )
private lazy var profileIconBackgroundView : UIView = {
let result : UIView = UIView ( )
result . isHidden = true
return result
} ( )
private lazy var profileIconImageView : UIImageView = {
let result : UIImageView = UIImageView ( )
result . contentMode = . scaleAspectFit
return result
} ( )
private lazy var additionalProfileIconBackgroundView : UIView = {
let result : UIView = UIView ( )
result . isHidden = true
return result
} ( )
private lazy var additionalProfileIconImageView : UIImageView = {
let result : UIImageView = UIImageView ( )
result . contentMode = . scaleAspectFit
return result
} ( )
// MARK: - L i f e c y c l e
// MARK: - L i f e c y c l e
public override init ( frame : CGRect ) {
public init ( size : Size ) {
super . init ( frame : frame )
self . size = size
super . init ( frame : CGRect ( x : 0 , y : 0 , width : size . viewSize , height : size . viewSize ) )
setUpViewHierarchy ( )
setUpViewHierarchy ( )
}
}
public required init ? ( coder : NSCoder ) {
public required init ? ( coder : NSCoder ) {
super . init ( coder : coder )
preconditionFailure ( " Use init(size:) instead. " )
setUpViewHierarchy ( )
}
}
private func setUpViewHierarchy ( ) {
private func setUpViewHierarchy ( ) {
let imageViewSize = CGFloat ( Values . mediumProfilePictureSize )
let additionalImageViewSize = CGFloat ( Values . smallProfilePictureSize )
addSubview ( imageContainerView )
addSubview ( imageContainerView )
addSubview ( profileIconBackgroundView )
addSubview ( additionalImageContainerView )
addSubview ( additionalImageContainerView )
addSubview ( additionalProfileIconBackgroundView )
imageContainerView . pin ( . leading , to : . leading , of : self )
imageContainerView . pin ( . top , to : . top , of : self )
profileIconBackgroundView . addSubview ( profileIconImageView )
imageViewWidthConstraint = imageContainerView . set ( . width , to : imageViewSize )
additionalProfileIconBackgroundView . addSubview ( additionalProfileIconImageView )
imageViewHeightConstraint = imageContainerView . set ( . height , to : imageViewSize )
widthConstraint = self . set ( . width , to : self . size . viewSize )
heightConstraint = self . set ( . height , to : self . size . viewSize )
imageViewTopConstraint = imageContainerView . pin ( . top , to : . top , of : self )
imageViewLeadingConstraint = imageContainerView . pin ( . leading , to : . leading , of : self )
imageViewCenterXConstraint = imageContainerView . center ( . horizontal , in : self )
imageViewCenterXConstraint . isActive = false
imageViewCenterYConstraint = imageContainerView . center ( . vertical , in : self )
imageViewCenterYConstraint . isActive = false
imageViewWidthConstraint = imageContainerView . set ( . width , to : size . imageSize )
imageViewHeightConstraint = imageContainerView . set ( . height , to : size . imageSize )
additionalImageContainerView . pin ( . trailing , to : . trailing , of : self )
additionalImageContainerView . pin ( . trailing , to : . trailing , of : self )
additionalImageContainerView . pin ( . bottom , to : . bottom , of : self )
additionalImageContainerView . pin ( . bottom , to : . bottom , of : self )
additionalImageViewWidthConstraint = additionalImageContainerView . set ( . width , to : additionalImageViewSize )
additionalImageViewWidthConstraint = additionalImageContainerView . set ( . width , to : size. multiImage Size)
additionalImageViewHeightConstraint = additionalImageContainerView . set ( . height , to : additionalImageViewSize )
additionalImageViewHeightConstraint = additionalImageContainerView . set ( . height , to : size. multiImage Size)
imageContainerView . addSubview ( imageView )
imageContainerView . addSubview ( imageView )
imageContainerView . addSubview ( animatedImageView )
imageContainerView . addSubview ( animatedImageView )
@ -131,32 +262,137 @@ public final class ProfilePictureView: UIView {
additionalProfilePlaceholderImageView . pin ( . left , to : . left , of : additionalImageContainerView )
additionalProfilePlaceholderImageView . pin ( . left , to : . left , of : additionalImageContainerView )
additionalProfilePlaceholderImageView . pin ( . right , to : . right , of : additionalImageContainerView )
additionalProfilePlaceholderImageView . pin ( . right , to : . right , of : additionalImageContainerView )
additionalProfilePlaceholderImageView . pin ( . bottom , to : . bottom , of : additionalImageContainerView , withInset : 5 )
additionalProfilePlaceholderImageView . pin ( . bottom , to : . bottom , of : additionalImageContainerView , withInset : 5 )
profileIconTopConstraint = profileIconImageView . pin (
. top ,
to : . top ,
of : profileIconBackgroundView ,
withInset : size . iconVerticalInset
)
profileIconImageView . pin ( . left , to : . left , of : profileIconBackgroundView )
profileIconImageView . pin ( . right , to : . right , of : profileIconBackgroundView )
profileIconBottomConstraint = profileIconImageView . pin (
. bottom ,
to : . bottom ,
of : profileIconBackgroundView ,
withInset : - size . iconVerticalInset
)
profileIconBackgroundLeftAlignConstraint = profileIconBackgroundView . pin ( . leading , to : . leading , of : imageContainerView )
profileIconBackgroundRightAlignConstraint = profileIconBackgroundView . pin ( . trailing , to : . trailing , of : imageContainerView )
profileIconBackgroundView . pin ( . bottom , to : . bottom , of : imageContainerView )
profileIconBackgroundWidthConstraint = profileIconBackgroundView . set ( . width , to : size . iconSize )
profileIconBackgroundHeightConstraint = profileIconBackgroundView . set ( . height , to : size . iconSize )
profileIconBackgroundLeftAlignConstraint . isActive = false
profileIconBackgroundRightAlignConstraint . isActive = false
additionalProfileIconTopConstraint = additionalProfileIconImageView . pin (
. top ,
to : . top ,
of : additionalProfileIconBackgroundView ,
withInset : size . iconVerticalInset
)
additionalProfileIconImageView . pin ( . left , to : . left , of : additionalProfileIconBackgroundView )
additionalProfileIconImageView . pin ( . right , to : . right , of : additionalProfileIconBackgroundView )
additionalProfileIconBottomConstraint = additionalProfileIconImageView . pin (
. bottom ,
to : . bottom ,
of : additionalProfileIconBackgroundView ,
withInset : - size . iconVerticalInset
)
additionalProfileIconBackgroundLeftAlignConstraint = additionalProfileIconBackgroundView . pin ( . leading , to : . leading , of : additionalImageContainerView )
additionalProfileIconBackgroundRightAlignConstraint = additionalProfileIconBackgroundView . pin ( . trailing , to : . trailing , of : additionalImageContainerView )
additionalProfileIconBackgroundView . pin ( . bottom , to : . bottom , of : additionalImageContainerView )
additionalProfileIconBackgroundWidthConstraint = additionalProfileIconBackgroundView . set ( . width , to : size . iconSize )
additionalProfileIconBackgroundHeightConstraint = additionalProfileIconBackgroundView . set ( . height , to : size . iconSize )
additionalProfileIconBackgroundLeftAlignConstraint . isActive = false
additionalProfileIconBackgroundRightAlignConstraint . isActive = false
}
// MARK: - C o n t e n t
private func updateIconView (
icon : ProfileIcon ,
imageView : UIImageView ,
backgroundView : UIView ,
leftAlignConstraint : NSLayoutConstraint ,
rightAlignConstraint : NSLayoutConstraint
) {
backgroundView . isHidden = ( icon = = . none )
leftAlignConstraint . isActive = (
icon = = . none ||
icon = = . crown
)
rightAlignConstraint . isActive = (
icon = = . rightPlus
)
switch icon {
case . none : imageView . image = nil
case . crown :
imageView . image = UIImage ( systemName : " crown.fill " )
backgroundView . themeBackgroundColor = . profileIcon_background
ThemeManager . onThemeChange ( observer : imageView ) { [ weak imageView ] _ , primaryColor in
let targetColor : ThemeValue = ( primaryColor = = . green ?
. profileIcon_greenPrimaryColor :
. profileIcon
)
guard imageView ? . themeTintColor != targetColor else { return }
imageView ? . themeTintColor = targetColor
}
case . rightPlus :
imageView . image = UIImage ( systemName : " plus " )
imageView . themeTintColor = . black
backgroundView . themeBackgroundColor = . primary
}
}
}
public func update (
public func update (
publicKey : String = " " ,
publicKey : String = " " ,
profile : Profile ? = nil ,
profile : Profile ? = nil ,
icon : ProfileIcon = . none ,
additionalProfile : Profile ? = nil ,
additionalProfile : Profile ? = nil ,
additionalIcon : ProfileIcon = . none ,
threadVariant : SessionThread . Variant ,
threadVariant : SessionThread . Variant ,
openGroupProfilePictureData : Data ? = nil ,
openGroupProfilePictureData : Data ? = nil ,
useFallbackPicture : Bool = false ,
useFallbackPicture : Bool = false ,
showMultiAvatarForClosedGroup : Bool = false
showMultiAvatarForClosedGroup : Bool = false
) {
) {
AssertIsOnMainThread ( )
AssertIsOnMainThread ( )
// S o r t o u t t h e p r o f i l e i c o n f i r s t
updateIconView (
icon : icon ,
imageView : profileIconImageView ,
backgroundView : profileIconBackgroundView ,
leftAlignConstraint : profileIconBackgroundLeftAlignConstraint ,
rightAlignConstraint : profileIconBackgroundRightAlignConstraint
)
guard ! useFallbackPicture else {
guard ! useFallbackPicture else {
switch self . size {
switch self . size {
case Values . smallProfilePictureSize . . < Values . mediumProfilePictureSize : imageView . image = # imageLiteral ( resourceName : " SessionWhite16 " )
case . navigation , . messag e: imageView . image = # imageLiteral ( resourceName : " SessionWhite16 " )
case Values . mediumProfilePictureSize . . < Values . largeProfilePictureSize : imageView . image = # imageLiteral ( resourceName : " SessionWhite24 " )
case . list: imageView . image = # imageLiteral ( resourceName : " SessionWhite24 " )
default : imageView . image = # imageLiteral ( resourceName : " SessionWhite40 " )
case . hero : imageView . image = # imageLiteral ( resourceName : " SessionWhite40 " )
}
}
imageView . contentMode = . center
imageView . contentMode = . center
imageView . isHidden = false
imageView . isHidden = false
animatedImageView . isHidden = true
animatedImageView . isHidden = true
imageContainerView . themeBackgroundColorForced = . theme ( . classicDark , color : . borderSeparator )
imageContainerView . themeBackgroundColorForced = . theme ( . classicDark , color : . borderSeparator )
imageContainerView . layer . cornerRadius = ( self . size / 2 )
imageContainerView . layer . cornerRadius = ( self . size . imageSize / 2 )
imageViewWidthConstraint . constant = self . size
imageViewWidthConstraint . constant = self . size . imageSize
imageViewHeightConstraint . constant = self . size
imageViewHeightConstraint . constant = self . size . imageSize
profileIconBackgroundWidthConstraint . constant = self . size . iconSize
profileIconBackgroundHeightConstraint . constant = self . size . iconSize
profileIconBackgroundView . layer . cornerRadius = ( self . size . iconSize / 2 )
additionalProfileIconBackgroundWidthConstraint . constant = self . size . iconSize
additionalProfileIconBackgroundHeightConstraint . constant = self . size . iconSize
additionalProfileIconBackgroundView . layer . cornerRadius = ( self . size . iconSize / 2 )
additionalImageContainerView . isHidden = true
additionalImageContainerView . isHidden = true
animatedImageView . image = nil
animatedImageView . image = nil
additionalImageView . image = nil
additionalImageView . image = nil
@ -203,25 +439,25 @@ public final class ProfilePictureView: UIView {
switch ( threadVariant , showMultiAvatarForClosedGroup ) {
switch ( threadVariant , showMultiAvatarForClosedGroup ) {
case ( . closedGroup , true ) :
case ( . closedGroup , true ) :
if self . size = = 40 {
targetSize = self . size . multiImageSize
targetSize = 32
}
else if self . size = = Values . largeProfilePictureSize {
targetSize = 56
}
else {
targetSize = Values . smallProfilePictureSize
}
imageViewWidthConstraint . constant = targetSize
imageViewHeightConstraint . constant = targetSize
additionalImageViewWidthConstraint . constant = targetSize
additionalImageViewHeightConstraint . constant = targetSize
additionalImageContainerView . isHidden = false
additionalImageContainerView . isHidden = false
imageViewTopConstraint . isActive = true
imageViewLeadingConstraint . isActive = true
imageViewCenterXConstraint . isActive = false
imageViewCenterYConstraint . isActive = false
// S o r t o u t t h e a d d i t i n o a l p r o f i l e i c o n i f n e e d e d
updateIconView (
icon : additionalIcon ,
imageView : additionalProfileIconImageView ,
backgroundView : additionalProfileIconBackgroundView ,
leftAlignConstraint : additionalProfileIconBackgroundLeftAlignConstraint ,
rightAlignConstraint : additionalProfileIconBackgroundRightAlignConstraint
)
if let additionalProfile : Profile = additionalProfile {
if let additionalProfile : Profile = additionalProfile {
let ( image , animatedImage , _ ) : ( UIImage ? , YYImage ? , Bool ) = getProfilePicture (
let ( image , animatedImage , _ ) : ( UIImage ? , YYImage ? , Bool ) = getProfilePicture (
of : targetSize ,
of : self . size. mul tiIm ageSize,
for : additionalProfile . id ,
for : additionalProfile . id ,
profile : additionalProfile
profile : additionalProfile
)
)
@ -241,15 +477,19 @@ public final class ProfilePictureView: UIView {
}
}
default :
default :
targetSize = self . size
targetSize = self . size . imageSize
imageViewWidthConstraint . constant = targetSize
imageViewHeightConstraint . constant = targetSize
additionalImageContainerView . isHidden = true
additionalImageContainerView . isHidden = true
additionalProfileIconBackgroundView . isHidden = true
additionalImageView . image = nil
additionalImageView . image = nil
additionalImageView . isHidden = true
additionalImageView . isHidden = true
additionalAnimatedImageView . image = nil
additionalAnimatedImageView . image = nil
additionalAnimatedImageView . isHidden = true
additionalAnimatedImageView . isHidden = true
additionalProfilePlaceholderImageView . isHidden = true
additionalProfilePlaceholderImageView . isHidden = true
imageViewTopConstraint . isActive = false
imageViewLeadingConstraint . isActive = false
imageViewCenterXConstraint . isActive = true
imageViewCenterYConstraint . isActive = true
}
}
// S e t t h e i m a g e
// S e t t h e i m a g e
@ -287,8 +527,14 @@ public final class ProfilePictureView: UIView {
imageView . contentMode = . scaleAspectFill
imageView . contentMode = . scaleAspectFill
animatedImageView . contentMode = . scaleAspectFill
animatedImageView . contentMode = . scaleAspectFill
imageContainerView . themeBackgroundColor = . backgroundSecondary
imageContainerView . themeBackgroundColor = . backgroundSecondary
imageViewWidthConstraint . constant = targetSize
imageViewHeightConstraint . constant = targetSize
imageContainerView . layer . cornerRadius = ( targetSize / 2 )
imageContainerView . layer . cornerRadius = ( targetSize / 2 )
additionalImageViewWidthConstraint . constant = targetSize
additionalImageViewHeightConstraint . constant = targetSize
additionalImageContainerView . layer . cornerRadius = ( targetSize / 2 )
additionalImageContainerView . layer . cornerRadius = ( targetSize / 2 )
profileIconBackgroundView . layer . cornerRadius = ( size . iconSize / 2 )
additionalProfileIconBackgroundView . layer . cornerRadius = ( size . iconSize / 2 )
}
}
// MARK: - C o n v e n i e n c e
// MARK: - C o n v e n i e n c e