session-ios/SessionUIKit/Components/ProfilePictureView.swift

584 lines
25 KiB
Swift
Raw Normal View History

// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import UIKit
import GRDB
Fixed a number of reported bugs, some cleanup, added animated profile support Added support for animated profile images (no ability to crop/resize) Updated the message trimming to only remove messages if the open group has 2000 messages or more Updated the message trimming setting to default to be on Updated the ContextMenu to fade out the snapshot as well (looked buggy if the device had even minor lag) Updated the ProfileManager to delete and re-download invalid avatar images (and updated the conversation screen to reload when avatars complete downloading) Updated the message request notification logic so it will show notifications when receiving a new message request as long as the user has read all the old ones (previously the user had to accept/reject all the old ones) Fixed a bug where the "trim open group messages" toggle was accessing UI off the main thread Fixed a bug where the "Chats" settings screen had a close button instead of a back button Fixed a bug where the 'viewsToMove' for the reply UI was inconsistent in some places Fixed an issue where the ProfileManager was doing all of it's validation (and writing to disk) within the database write closure which would block database writes unnecessarily Fixed a bug where a message request wouldn't be identified as such just because it wasn't visible in the conversations list Fixed a bug where opening a message request notification would result in the message request being in the wrong state (also wouldn't insert the 'MessageRequestsViewController' into the hierarchy) Fixed a bug where the avatar image wouldn't appear beside incoming closed group message in some situations cases Removed an error log that was essentially just spam Remove the logic to delete old profile images when calling save on a Profile (wouldn't get called if the row was modified directly and duplicates GarbageCollection logic) Remove the logic to send a notification when calling save on a Profile (wouldn't get called if the row was modified directly) Tweaked the message trimming description to be more accurate Cleaned up some duplicate logic used to determine if a notification should be shown Cleaned up some onion request logic (was passing the version info in some cases when not needed) Moved the push notification notify API call into the PushNotificationAPI class for consistency
2022-07-08 09:53:48 +02:00
import YYImage
2019-11-28 06:42:07 +01:00
public final class ProfilePictureView: UIView {
public struct Info {
let imageData: Data?
let renderingMode: UIImage.RenderingMode
let themeTintColor: ThemeValue?
let inset: UIEdgeInsets
let icon: ProfileIcon
let backgroundColor: ThemeValue?
let forcedBackgroundColor: ForcedThemeValue?
public init(
imageData: Data?,
renderingMode: UIImage.RenderingMode = .automatic,
themeTintColor: ThemeValue? = nil,
inset: UIEdgeInsets = .zero,
icon: ProfileIcon = .none,
backgroundColor: ThemeValue? = nil,
forcedBackgroundColor: ForcedThemeValue? = nil
) {
self.imageData = imageData
self.renderingMode = renderingMode
self.themeTintColor = themeTintColor
self.inset = inset
self.icon = icon
self.backgroundColor = backgroundColor
self.forcedBackgroundColor = forcedBackgroundColor
}
}
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
}
}
public var imageSize: CGFloat {
switch self {
case .navigation, .message: return 26
case .list: return 46
case .hero: return 80
}
}
public var multiImageSize: CGFloat {
switch self {
case .navigation, .message: return 18 // Shouldn't be used
case .list: return 32
case .hero: return 80
}
}
var iconSize: CGFloat {
switch self {
case .navigation, .message: return 10 // Intentionally not a multiple of 4
case .list: return 16
case .hero: return 24
}
}
}
public enum ProfileIcon: Equatable, Hashable {
case none
case crown
case rightPlus
func iconVerticalInset(for size: Size) -> CGFloat {
switch (self, size) {
case (.crown, .navigation), (.crown, .message): return 1
case (.crown, .list): return 3
case (.crown, .hero): return 5
case (.rightPlus, _): return 3
default: return 0
}
}
}
public var size: Size {
didSet {
widthConstraint.constant = (customWidth ?? size.viewSize)
heightConstraint.constant = size.viewSize
profileIconBackgroundWidthConstraint.constant = size.iconSize
profileIconBackgroundHeightConstraint.constant = size.iconSize
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)
}
}
override public var clipsToBounds: Bool {
didSet {
imageContainerView.clipsToBounds = clipsToBounds
additionalImageContainerView.clipsToBounds = clipsToBounds
imageContainerView.layer.cornerRadius = (clipsToBounds ?
(additionalImageContainerView.isHidden ? (size.imageSize / 2) : (size.multiImageSize / 2)) :
0
)
imageContainerView.layer.cornerRadius = (clipsToBounds ? (size.multiImageSize / 2) : 0)
}
}
public override var isHidden: Bool {
didSet {
widthConstraint.constant = (isHidden ? 0 : size.viewSize)
heightConstraint.constant = (isHidden ? 0 : size.viewSize)
}
}
// MARK: - Constraints
private var widthConstraint: NSLayoutConstraint!
private var heightConstraint: NSLayoutConstraint!
private var imageViewTopConstraint: NSLayoutConstraint!
private var imageViewLeadingConstraint: NSLayoutConstraint!
private var imageViewCenterXConstraint: NSLayoutConstraint!
private var imageViewCenterYConstraint: NSLayoutConstraint!
2019-11-28 06:42:07 +01:00
private var imageViewWidthConstraint: NSLayoutConstraint!
private var imageViewHeightConstraint: NSLayoutConstraint!
2020-09-14 03:31:45 +02:00
private var additionalImageViewWidthConstraint: 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!
private lazy var imageEdgeConstraints: [NSLayoutConstraint] = [ // MUST be in 'top, left, bottom, right' order
imageView.pin(.top, to: .top, of: imageContainerView, withInset: 0),
imageView.pin(.left, to: .left, of: imageContainerView, withInset: 0),
imageView.pin(.bottom, to: .bottom, of: imageContainerView, withInset: 0),
imageView.pin(.right, to: .right, of: imageContainerView, withInset: 0),
animatedImageView.pin(.top, to: .top, of: imageContainerView, withInset: 0),
animatedImageView.pin(.left, to: .left, of: imageContainerView, withInset: 0),
animatedImageView.pin(.bottom, to: .bottom, of: imageContainerView, withInset: 0),
animatedImageView.pin(.right, to: .right, of: imageContainerView, withInset: 0)
]
private lazy var additionalImageEdgeConstraints: [NSLayoutConstraint] = [ // MUST be in 'top, left, bottom, right' order
additionalImageView.pin(.top, to: .top, of: additionalImageContainerView, withInset: 0),
additionalImageView.pin(.left, to: .left, of: additionalImageContainerView, withInset: 0),
additionalImageView.pin(.bottom, to: .bottom, of: additionalImageContainerView, withInset: 0),
additionalImageView.pin(.right, to: .right, of: additionalImageContainerView, withInset: 0),
additionalAnimatedImageView.pin(.top, to: .top, of: additionalImageContainerView, withInset: 0),
additionalAnimatedImageView.pin(.left, to: .left, of: additionalImageContainerView, withInset: 0),
additionalAnimatedImageView.pin(.bottom, to: .bottom, of: additionalImageContainerView, withInset: 0),
additionalAnimatedImageView.pin(.right, to: .right, of: additionalImageContainerView, withInset: 0)
]
2019-11-28 06:42:07 +01:00
// MARK: - Components
private lazy var imageContainerView: UIView = {
let result: UIView = UIView()
result.translatesAutoresizingMaskIntoConstraints = false
result.clipsToBounds = true
result.themeBackgroundColor = .backgroundSecondary
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.themeBackgroundColor = .primary
result.themeBorderColor = .backgroundPrimary
result.layer.borderWidth = 1
result.isHidden = true
return result
}()
private lazy var additionalImageView: UIImageView = {
let result: UIImageView = UIImageView()
result.translatesAutoresizingMaskIntoConstraints = false
result.contentMode = .scaleAspectFill
Merge branch 'dev' into feature/theming # Conflicts: # Podfile.lock # Session.xcodeproj/project.pbxproj # Session/Conversations/Message Cells/VisibleMessageCell.swift # Session/Meta/Translations/de.lproj/Localizable.strings # Session/Meta/Translations/en.lproj/Localizable.strings # Session/Meta/Translations/es.lproj/Localizable.strings # Session/Meta/Translations/fa.lproj/Localizable.strings # Session/Meta/Translations/fi.lproj/Localizable.strings # Session/Meta/Translations/fr.lproj/Localizable.strings # Session/Meta/Translations/hi.lproj/Localizable.strings # Session/Meta/Translations/hr.lproj/Localizable.strings # Session/Meta/Translations/id-ID.lproj/Localizable.strings # Session/Meta/Translations/it.lproj/Localizable.strings # Session/Meta/Translations/ja.lproj/Localizable.strings # Session/Meta/Translations/nl.lproj/Localizable.strings # Session/Meta/Translations/pl.lproj/Localizable.strings # Session/Meta/Translations/pt_BR.lproj/Localizable.strings # Session/Meta/Translations/ru.lproj/Localizable.strings # Session/Meta/Translations/si.lproj/Localizable.strings # Session/Meta/Translations/sk.lproj/Localizable.strings # Session/Meta/Translations/sv.lproj/Localizable.strings # Session/Meta/Translations/th.lproj/Localizable.strings # Session/Meta/Translations/vi-VN.lproj/Localizable.strings # Session/Meta/Translations/zh-Hant.lproj/Localizable.strings # Session/Meta/Translations/zh_CN.lproj/Localizable.strings # Session/Notifications/AppNotifications.swift # SessionMessagingKit/Shared Models/MessageViewModel.swift # SessionMessagingKit/Shared Models/SessionThreadViewModel.swift # SignalUtilitiesKit/Profile Pictures/ProfilePictureView.swift
2022-08-26 05:24:42 +02:00
result.themeTintColor = .textPrimary
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
}()
2019-11-28 06:42:07 +01:00
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: - Lifecycle
public init(size: Size) {
self.size = size
super.init(frame: CGRect(x: 0, y: 0, width: size.viewSize, height: size.viewSize))
clipsToBounds = true
2019-12-02 01:58:15 +01:00
setUpViewHierarchy()
2019-11-28 06:42:07 +01:00
}
2020-01-20 03:20:27 +01:00
public required init?(coder: NSCoder) {
preconditionFailure("Use init(size:) instead.")
2019-11-28 06:42:07 +01:00
}
2019-12-02 01:58:15 +01:00
private func setUpViewHierarchy() {
addSubview(imageContainerView)
addSubview(profileIconBackgroundView)
addSubview(additionalImageContainerView)
addSubview(additionalProfileIconBackgroundView)
profileIconBackgroundView.addSubview(profileIconImageView)
additionalProfileIconBackgroundView.addSubview(additionalProfileIconImageView)
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(.bottom, to: .bottom, of: self)
additionalImageViewWidthConstraint = additionalImageContainerView.set(.width, to: size.multiImageSize)
additionalImageViewHeightConstraint = additionalImageContainerView.set(.height, to: size.multiImageSize)
imageContainerView.addSubview(imageView)
imageContainerView.addSubview(animatedImageView)
additionalImageContainerView.addSubview(additionalImageView)
additionalImageContainerView.addSubview(additionalAnimatedImageView)
// Activate the image edge constraints
imageEdgeConstraints.forEach { $0.isActive = true }
additionalImageEdgeConstraints.forEach { $0.isActive = true }
profileIconTopConstraint = profileIconImageView.pin(
.top,
to: .top,
of: profileIconBackgroundView,
withInset: 0
)
profileIconImageView.pin(.left, to: .left, of: profileIconBackgroundView)
profileIconImageView.pin(.right, to: .right, of: profileIconBackgroundView)
profileIconBottomConstraint = profileIconImageView.pin(
.bottom,
to: .bottom,
of: profileIconBackgroundView,
withInset: 0
)
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: 0
)
additionalProfileIconImageView.pin(.left, to: .left, of: additionalProfileIconBackgroundView)
additionalProfileIconImageView.pin(.right, to: .right, of: additionalProfileIconBackgroundView)
additionalProfileIconBottomConstraint = additionalProfileIconImageView.pin(
.bottom,
to: .bottom,
of: additionalProfileIconBackgroundView,
withInset: 0
)
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: - Content
private func updateIconView(
icon: ProfileIcon,
imageView: UIImageView,
backgroundView: UIView,
topConstraint: NSLayoutConstraint,
leftAlignConstraint: NSLayoutConstraint,
rightAlignConstraint: NSLayoutConstraint,
bottomConstraint: NSLayoutConstraint
) {
backgroundView.isHidden = (icon == .none)
leftAlignConstraint.isActive = (
icon == .none ||
icon == .crown
)
rightAlignConstraint.isActive = (
icon == .rightPlus
)
topConstraint.constant = icon.iconVerticalInset(for: size)
bottomConstraint.constant = -icon.iconVerticalInset(for: size)
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",
withConfiguration: UIImage.SymbolConfiguration(weight: .semibold)
)
imageView.themeTintColor = .black
2023-05-25 00:42:27 +02:00
backgroundView.themeBackgroundColor = .primary
}
}
// MARK: - Content
private func prepareForReuse() {
imageView.contentMode = .scaleAspectFill
imageView.isHidden = true
animatedImageView.contentMode = .scaleAspectFill
animatedImageView.isHidden = true
imageContainerView.clipsToBounds = clipsToBounds
imageContainerView.themeBackgroundColor = .backgroundSecondary
additionalImageContainerView.isHidden = true
animatedImageView.image = nil
additionalImageView.image = nil
additionalAnimatedImageView.image = nil
additionalImageView.isHidden = true
additionalAnimatedImageView.isHidden = true
additionalImageContainerView.clipsToBounds = clipsToBounds
imageViewTopConstraint.isActive = false
imageViewLeadingConstraint.isActive = false
imageViewCenterXConstraint.isActive = true
imageViewCenterYConstraint.isActive = true
profileIconBackgroundView.isHidden = true
profileIconBackgroundLeftAlignConstraint.isActive = false
profileIconBackgroundRightAlignConstraint.isActive = false
additionalProfileIconBackgroundView.isHidden = true
additionalProfileIconBackgroundLeftAlignConstraint.isActive = false
additionalProfileIconBackgroundRightAlignConstraint.isActive = false
imageEdgeConstraints.forEach { $0.constant = 0 }
additionalImageEdgeConstraints.forEach { $0.constant = 0 }
}
public func update(
_ info: Info,
additionalInfo: Info? = nil
) {
prepareForReuse()
// Sort out the icon first
updateIconView(
icon: info.icon,
imageView: profileIconImageView,
backgroundView: profileIconBackgroundView,
topConstraint: profileIconTopConstraint,
leftAlignConstraint: profileIconBackgroundLeftAlignConstraint,
rightAlignConstraint: profileIconBackgroundRightAlignConstraint,
bottomConstraint: profileIconBottomConstraint
)
// Populate the main imageView
switch info.imageData?.guessedImageFormat {
case .gif, .webp: animatedImageView.image = info.imageData.map { YYImage(data: $0) }
default:
imageView.image = info.imageData
.map {
guard info.renderingMode != .automatic else { return UIImage(data: $0) }
return UIImage(data: $0)?.withRenderingMode(info.renderingMode)
}
}
imageView.themeTintColor = info.themeTintColor
imageView.isHidden = (imageView.image == nil)
animatedImageView.themeTintColor = info.themeTintColor
animatedImageView.isHidden = (animatedImageView.image == nil)
imageContainerView.themeBackgroundColor = info.backgroundColor
imageContainerView.themeBackgroundColorForced = info.forcedBackgroundColor
profileIconBackgroundView.layer.cornerRadius = (size.iconSize / 2)
imageEdgeConstraints.enumerated().forEach { index, constraint in
switch index % 4 {
case 0: constraint.constant = info.inset.top
case 1: constraint.constant = info.inset.left
case 2: constraint.constant = -info.inset.bottom
case 3: constraint.constant = -info.inset.right
default: break
2020-09-14 03:31:45 +02:00
}
2019-11-29 06:30:01 +01:00
}
// Check if there is a second image (if not then set the size and finish)
guard let additionalInfo: Info = additionalInfo else {
imageViewWidthConstraint.constant = size.imageSize
imageViewHeightConstraint.constant = size.imageSize
imageContainerView.layer.cornerRadius = (imageContainerView.clipsToBounds ? (size.imageSize / 2) : 0)
return
}
// Sort out the additional icon first
updateIconView(
icon: additionalInfo.icon,
imageView: additionalProfileIconImageView,
backgroundView: additionalProfileIconBackgroundView,
topConstraint: additionalProfileIconTopConstraint,
leftAlignConstraint: additionalProfileIconBackgroundLeftAlignConstraint,
rightAlignConstraint: additionalProfileIconBackgroundRightAlignConstraint,
bottomConstraint: additionalProfileIconBottomConstraint
)
// Set the additional image content and reposition the image views correctly
switch additionalInfo.imageData?.guessedImageFormat {
case .gif, .webp: additionalAnimatedImageView.image = additionalInfo.imageData.map { YYImage(data: $0) }
default:
additionalImageView.image = additionalInfo.imageData
.map {
guard additionalInfo.renderingMode != .automatic else { return UIImage(data: $0) }
return UIImage(data: $0)?.withRenderingMode(additionalInfo.renderingMode)
}
2019-11-28 06:42:07 +01:00
}
additionalImageView.themeTintColor = additionalInfo.themeTintColor
additionalImageView.isHidden = (additionalImageView.image == nil)
additionalAnimatedImageView.themeTintColor = additionalInfo.themeTintColor
additionalAnimatedImageView.isHidden = (additionalAnimatedImageView.image == nil)
additionalImageContainerView.isHidden = false
switch (info.backgroundColor, info.forcedBackgroundColor) {
case (_, .some(let color)): additionalImageContainerView.themeBackgroundColorForced = color
case (.some(let color), _): additionalImageContainerView.themeBackgroundColor = color
default: additionalImageContainerView.themeBackgroundColor = .primary
}
additionalImageEdgeConstraints.enumerated().forEach { index, constraint in
switch index % 4 {
case 0: constraint.constant = additionalInfo.inset.top
case 1: constraint.constant = additionalInfo.inset.left
case 2: constraint.constant = -additionalInfo.inset.bottom
case 3: constraint.constant = -additionalInfo.inset.right
default: break
}
}
imageViewTopConstraint.isActive = true
imageViewLeadingConstraint.isActive = true
imageViewCenterXConstraint.isActive = false
imageViewCenterYConstraint.isActive = false
imageViewWidthConstraint.constant = size.multiImageSize
imageViewHeightConstraint.constant = size.multiImageSize
imageContainerView.layer.cornerRadius = (imageContainerView.clipsToBounds ? (size.multiImageSize / 2) : 0)
additionalImageViewWidthConstraint.constant = size.multiImageSize
additionalImageViewHeightConstraint.constant = size.multiImageSize
additionalImageContainerView.layer.cornerRadius = (additionalImageContainerView.clipsToBounds ?
(size.multiImageSize / 2) :
0
)
additionalProfileIconBackgroundView.layer.cornerRadius = (size.iconSize / 2)
2019-11-28 06:42:07 +01:00
}
}
2023-08-07 02:36:40 +02:00
import SwiftUI
2023-08-07 03:32:15 +02:00
public struct ProfilePictureSwiftUI: UIViewRepresentable {
public typealias UIViewType = ProfilePictureView
2023-08-07 02:36:40 +02:00
var size: ProfilePictureView.Size
var info: ProfilePictureView.Info
var additionalInfo: ProfilePictureView.Info?
public init(
size: ProfilePictureView.Size,
info: ProfilePictureView.Info,
additionalInfo: ProfilePictureView.Info? = nil
) {
self.size = size
self.info = info
self.additionalInfo = additionalInfo
}
2023-08-07 03:32:15 +02:00
public func makeUIView(context: Context) -> ProfilePictureView {
2023-08-07 02:36:40 +02:00
ProfilePictureView(size: size)
}
2023-08-07 03:32:15 +02:00
public func updateUIView(_ profilePictureView: ProfilePictureView, context: Context) {
2023-08-07 02:36:40 +02:00
profilePictureView.update(
info,
additionalInfo: additionalInfo
)
}
}