final class FakeChatView : UIView {
private let spacing = Values . mediumSpacing
private lazy var chatBubbles = [
getChatBubble ( withText : NSLocalizedString ( " What is Loki Messenger? A completely decentralised private messaging application for all platforms. " , comment : " " ) , wasSentByCurrentUser : true ) ,
getChatBubble ( withText : NSLocalizedString ( " So no metadata collection, or personally identifiable information? How does it work? " , comment : " " ) , wasSentByCurrentUser : false ) ,
getChatBubble ( withText : NSLocalizedString ( " Through a combination of advanced blockchain techniques including onion routing through Lokinet's private servers. " , comment : " " ) , wasSentByCurrentUser : true ) ,
getChatBubble ( withText : NSLocalizedString ( " Friends don't let friends use compromised messengers. You're welcome. " , comment : " " ) , wasSentByCurrentUser : true )
]
private lazy var scrollView : UIScrollView = {
let result = UIScrollView ( )
result . showsVerticalScrollIndicator = false
return result
} ( )
override init ( frame : CGRect ) {
super . init ( frame : frame )
setUpViewHierarchy ( )
animate ( )
}
required init ? ( coder : NSCoder ) {
super . init ( coder : coder )
setUpViewHierarchy ( )
animate ( )
}
private func setUpViewHierarchy ( ) {
let stackView = UIStackView ( arrangedSubviews : chatBubbles )
stackView . axis = . vertical
stackView . spacing = spacing
stackView . alignment = . fill
stackView . set ( . width , to : UIScreen . main . bounds . width )
stackView . layoutMargins = UIEdgeInsets ( top : 8 , leading : Values . veryLargeSpacing , bottom : 8 , trailing : Values . veryLargeSpacing )
stackView . isLayoutMarginsRelativeArrangement = true
scrollView . addSubview ( stackView )
stackView . pin ( to : scrollView )
addSubview ( scrollView )
scrollView . pin ( to : self )
}
private func getChatBubble ( withText text : String , wasSentByCurrentUser : Bool ) -> UIView {
let result = UIView ( )
let bubbleView = UIView ( )
bubbleView . set ( . width , to : Values . fakeChatBubbleWidth )
bubbleView . layer . cornerRadius = Values . fakeChatBubbleCornerRadius
bubbleView . layer . shadowColor = UIColor . black . cgColor
bubbleView . layer . shadowRadius = 8
bubbleView . layer . shadowOpacity = 0.64
let backgroundColor = wasSentByCurrentUser ? Colors . accent : Colors . fakeChatBubbleBackground
bubbleView . backgroundColor = backgroundColor
let label = UILabel ( )
let textColor = wasSentByCurrentUser ? Colors . fakeChatBubbleText : Colors . text
label . textColor = textColor
label . font = . boldSystemFont ( ofSize : Values . mediumFontSize )
label . numberOfLines = 0
label . lineBreakMode = . byWordWrapping
label . text = text
bubbleView . addSubview ( label )
label . pin ( to : bubbleView , withInset : 12 )
result . addSubview ( bubbleView )
bubbleView . pin ( . top , to : . top , of : result )
result . pin ( . bottom , to : . bottom , of : bubbleView )
if wasSentByCurrentUser {
bubbleView . pin ( . leading , to : . leading , of : result )
} else {
result . pin ( . trailing , to : . trailing , of : bubbleView )
}
return result
}
private func animate ( ) {
let animationDuration = Values . fakeChatAnimationDuration
let delayBetweenMessages = Values . fakeChatDelay
chatBubbles . forEach { $0 . alpha = 0 }
Timer . scheduledTimer ( withTimeInterval : Values . fakeChatStartDelay , repeats : false ) { [ weak self ] _ in
self ? . showChatBubble ( at : 0 )
Timer . scheduledTimer ( withTimeInterval : delayBetweenMessages , repeats : false ) { _ in
self ? . showChatBubble ( at : 1 )
Timer . scheduledTimer ( withTimeInterval : delayBetweenMessages , repeats : false ) { _ in
self ? . showChatBubble ( at : 2 )
UIView . animate ( withDuration : animationDuration ) {
guard let self = self else { return }
self . scrollView . contentOffset = CGPoint ( x : 0 , y : self . chatBubbles [ 0 ] . height ( ) + self . spacing )
}
Timer . scheduledTimer ( withTimeInterval : delayBetweenMessages , repeats : false ) { _ in
self ? . showChatBubble ( at : 3 )
UIView . animate ( withDuration : animationDuration ) {
guard let self = self else { return }
self . scrollView . contentOffset = CGPoint ( x : 0 , y : self . chatBubbles [ 0 ] . height ( ) + self . spacing + self . chatBubbles [ 1 ] . height ( ) + self . spacing )
}
}
}
}
}
}
private func showChatBubble ( at index : Int ) {
let chatBubble = chatBubbles [ index ]
UIView . animate ( withDuration : Values . fakeChatAnimationDuration ) {
chatBubble . alpha = 1
}
let scale = Values . fakeChatMessagePopAnimationStartScale
chatBubble . transform = CGAffineTransform ( scaleX : scale , y : scale )
UIView . animate ( withDuration : Values . fakeChatAnimationDuration , delay : 0 , usingSpringWithDamping : 0.68 , initialSpringVelocity : 4 , options : . curveEaseInOut , animations : {
chatBubble . transform = CGAffineTransform ( scaleX : 1 , y : 1 )
} , completion : nil )
}
}