mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Channel recommendation improvements
This commit is contained in:
parent
c688b5ad5f
commit
17a203d6b5
@ -29,6 +29,9 @@ public final class AvatarBadgeView: UIImageView {
|
||||
private struct Parameters: Equatable {
|
||||
var size: CGSize
|
||||
var text: String
|
||||
var hasTimeoutIcon: Bool
|
||||
var useSolidColor: Bool
|
||||
var strokeColor: UIColor?
|
||||
}
|
||||
|
||||
private var originalContent: OriginalContent?
|
||||
@ -50,8 +53,8 @@ public final class AvatarBadgeView: UIImageView {
|
||||
}
|
||||
}
|
||||
|
||||
public func update(size: CGSize, text: String) {
|
||||
let parameters = Parameters(size: size, text: text)
|
||||
public func update(size: CGSize, text: String, hasTimeoutIcon: Bool = true, useSolidColor: Bool = false, strokeColor: UIColor? = nil) {
|
||||
let parameters = Parameters(size: size, text: text, hasTimeoutIcon: hasTimeoutIcon, useSolidColor: useSolidColor, strokeColor: strokeColor)
|
||||
if self.parameters != parameters || !self.hasContent {
|
||||
self.parameters = parameters
|
||||
self.update()
|
||||
@ -192,24 +195,89 @@ public final class AvatarBadgeView: UIImageView {
|
||||
return
|
||||
}
|
||||
|
||||
self.image = generateImage(parameters.size, rotatedContext: { size, context in
|
||||
var solidColor: UIColor?
|
||||
if parameters.useSolidColor {
|
||||
let context = DrawingContext(size: CGSize(width: 1.0, height: 1.0), scale: 1.0, clear: false)!
|
||||
context.withFlippedContext({ context in
|
||||
if let cgImage = blurredImage.cgImage {
|
||||
context.draw(cgImage, in: CGRect(x: 0.0, y: 0.0, width: 1.0, height: 1.0))
|
||||
}
|
||||
})
|
||||
solidColor = context.colorAt(.zero)
|
||||
}
|
||||
|
||||
var badgeSize = parameters.size
|
||||
|
||||
let strokeWidth: CGFloat = 1.0 + UIScreenPixel
|
||||
var size = parameters.size
|
||||
var offset: CGPoint = .zero
|
||||
if parameters.strokeColor != nil {
|
||||
offset = CGPoint(x: strokeWidth / 2.0, y: strokeWidth / 2.0)
|
||||
badgeSize.width += strokeWidth
|
||||
badgeSize.height += strokeWidth
|
||||
size.width += strokeWidth * 2.0
|
||||
size.height += strokeWidth * 2.0
|
||||
}
|
||||
|
||||
self.image = generateImage(size, rotatedContext: { size, context in
|
||||
UIGraphicsPushContext(context)
|
||||
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
context.setBlendMode(.copy)
|
||||
context.setFillColor(UIColor.black.cgColor)
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
blurredImage.draw(in: CGRect(origin: CGPoint(), size: size), blendMode: .sourceIn, alpha: 1.0)
|
||||
|
||||
context.setBlendMode(.normal)
|
||||
|
||||
let textColor: UIColor
|
||||
if isLightImage {
|
||||
textColor = UIColor(white: 0.7, alpha: 1.0)
|
||||
} else {
|
||||
if parameters.useSolidColor {
|
||||
textColor = .white
|
||||
} else {
|
||||
if isLightImage {
|
||||
textColor = UIColor(white: 0.7, alpha: 1.0)
|
||||
} else {
|
||||
textColor = .white
|
||||
}
|
||||
}
|
||||
|
||||
if var solidColor {
|
||||
func adjustedBackgroundColor(backgroundColor: UIColor, textColor: UIColor) -> UIColor {
|
||||
let minContrastRatio: CGFloat = 4.5
|
||||
if backgroundColor.contrastRatio(with: textColor) < minContrastRatio {
|
||||
var hue: CGFloat = 0
|
||||
var saturation: CGFloat = 0
|
||||
var brightness: CGFloat = 0
|
||||
var alpha: CGFloat = 0
|
||||
backgroundColor.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha)
|
||||
return UIColor(hue: hue, saturation: saturation, brightness: brightness * 0.9, alpha: alpha)
|
||||
} else {
|
||||
return backgroundColor
|
||||
}
|
||||
}
|
||||
solidColor = adjustedBackgroundColor(backgroundColor: solidColor, textColor: textColor)
|
||||
|
||||
if let strokeColor = parameters.strokeColor {
|
||||
context.setStrokeColor(strokeColor.cgColor)
|
||||
context.setLineWidth(strokeWidth)
|
||||
}
|
||||
|
||||
context.setFillColor(solidColor.cgColor)
|
||||
} else {
|
||||
context.setBlendMode(.copy)
|
||||
context.setFillColor(UIColor.black.cgColor)
|
||||
}
|
||||
if badgeSize.width != badgeSize.height {
|
||||
let path = UIBezierPath(roundedRect: CGRect(origin: offset, size: badgeSize), cornerRadius: badgeSize.height / 2.0)
|
||||
context.addPath(path.cgPath)
|
||||
if let _ = parameters.strokeColor {
|
||||
context.drawPath(using: .fillStroke)
|
||||
} else {
|
||||
context.fillPath()
|
||||
}
|
||||
} else {
|
||||
context.fillEllipse(in: CGRect(origin: offset, size: badgeSize))
|
||||
}
|
||||
|
||||
if let _ = solidColor {
|
||||
|
||||
} else {
|
||||
blurredImage.draw(in: CGRect(origin: CGPoint(), size: badgeSize), blendMode: .sourceIn, alpha: 1.0)
|
||||
context.setBlendMode(.normal)
|
||||
}
|
||||
|
||||
var fontSize: CGFloat = floor(parameters.size.height * 0.48)
|
||||
@ -225,28 +293,30 @@ public final class AvatarBadgeView: UIImageView {
|
||||
}
|
||||
}
|
||||
|
||||
let lineWidth: CGFloat = 1.5
|
||||
let lineInset: CGFloat = 2.0
|
||||
let lineRadius: CGFloat = size.width * 0.5 - lineInset - lineWidth * 0.5
|
||||
context.setLineWidth(lineWidth)
|
||||
context.setStrokeColor(textColor.cgColor)
|
||||
context.setLineCap(.round)
|
||||
|
||||
context.addArc(center: CGPoint(x: size.width * 0.5, y: size.height * 0.5), radius: lineRadius, startAngle: CGFloat.pi * 0.5, endAngle: -CGFloat.pi * 0.5, clockwise: false)
|
||||
context.strokePath()
|
||||
|
||||
let sectionAngle: CGFloat = CGFloat.pi / 11.0
|
||||
|
||||
for i in 0 ..< 10 {
|
||||
if i % 2 == 0 {
|
||||
continue
|
||||
}
|
||||
if parameters.hasTimeoutIcon {
|
||||
let lineWidth: CGFloat = 1.5
|
||||
let lineInset: CGFloat = 2.0
|
||||
let lineRadius: CGFloat = size.width * 0.5 - lineInset - lineWidth * 0.5
|
||||
context.setLineWidth(lineWidth)
|
||||
context.setStrokeColor(textColor.cgColor)
|
||||
context.setLineCap(.round)
|
||||
|
||||
let startAngle = CGFloat.pi * 0.5 - CGFloat(i) * sectionAngle - sectionAngle * 0.15
|
||||
let endAngle = startAngle - sectionAngle * 0.75
|
||||
|
||||
context.addArc(center: CGPoint(x: size.width * 0.5, y: size.height * 0.5), radius: lineRadius, startAngle: startAngle, endAngle: endAngle, clockwise: true)
|
||||
context.addArc(center: CGPoint(x: size.width * 0.5, y: size.height * 0.5), radius: lineRadius, startAngle: CGFloat.pi * 0.5, endAngle: -CGFloat.pi * 0.5, clockwise: false)
|
||||
context.strokePath()
|
||||
|
||||
let sectionAngle: CGFloat = CGFloat.pi / 11.0
|
||||
|
||||
for i in 0 ..< 10 {
|
||||
if i % 2 == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
let startAngle = CGFloat.pi * 0.5 - CGFloat(i) * sectionAngle - sectionAngle * 0.15
|
||||
let endAngle = startAngle - sectionAngle * 0.75
|
||||
|
||||
context.addArc(center: CGPoint(x: size.width * 0.5, y: size.height * 0.5), radius: lineRadius, startAngle: startAngle, endAngle: endAngle, clockwise: true)
|
||||
context.strokePath()
|
||||
}
|
||||
}
|
||||
|
||||
/*if isLightImage {
|
||||
|
@ -172,6 +172,12 @@ public extension UIColor {
|
||||
}
|
||||
}
|
||||
|
||||
func contrastRatio(with other: UIColor) -> CGFloat {
|
||||
let l1 = self.lightness
|
||||
let l2 = other.lightness
|
||||
return (max(l1, l2) + 0.05) / (min(l1, l2) + 0.05)
|
||||
}
|
||||
|
||||
var brightness: CGFloat {
|
||||
var hue: CGFloat = 0.0
|
||||
var saturation: CGFloat = 0.0
|
||||
|
@ -22,6 +22,7 @@ import ChatMessageItemCommon
|
||||
import RoundedRectWithTailPath
|
||||
import AvatarNode
|
||||
import MultilineTextComponent
|
||||
import BundleIconComponent
|
||||
import ChatMessageBackground
|
||||
|
||||
private func attributedServiceMessageString(theme: ChatPresentationThemeData, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, message: EngineMessage, accountPeerId: EnginePeer.Id) -> NSAttributedString? {
|
||||
@ -545,6 +546,8 @@ private final class ChannelItemComponent: Component {
|
||||
private let title = ComponentView<Empty>()
|
||||
private let subtitle = ComponentView<Empty>()
|
||||
private let avatarNode: AvatarNode
|
||||
private let avatarBadge: AvatarBadgeView
|
||||
private let subtitleIcon = ComponentView<Empty>()
|
||||
|
||||
private var component: ChannelItemComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
@ -553,12 +556,17 @@ private final class ChannelItemComponent: Component {
|
||||
self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 26.0))
|
||||
self.avatarNode.isUserInteractionEnabled = false
|
||||
|
||||
self.avatarBadge = AvatarBadgeView(frame: CGRect())
|
||||
|
||||
self.containerButton = HighlightTrackingButton()
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.addSubview(self.containerButton)
|
||||
self.addSubnode(self.avatarNode)
|
||||
self.avatarNode.view.addSubview(self.avatarBadge)
|
||||
|
||||
self.avatarNode.badgeView = self.avatarBadge
|
||||
|
||||
self.containerButton.addTarget(self, action: #selector(self.pressed), for: .touchUpInside)
|
||||
}
|
||||
@ -581,25 +589,38 @@ private final class ChannelItemComponent: Component {
|
||||
let titleSize = self.title.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: component.peer.compactDisplayTitle, font: Font.regular(11.0), textColor: component.theme.chat.message.incoming.primaryTextColor))
|
||||
text: .plain(NSAttributedString(string: component.peer.compactDisplayTitle, font: Font.regular(11.0), textColor: component.theme.chat.message.incoming.primaryTextColor)),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 2
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: itemSize.width - 20.0, height: 100.0)
|
||||
containerSize: CGSize(width: itemSize.width - 16.0, height: 100.0)
|
||||
)
|
||||
|
||||
let subtitleSize = self.subtitle.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: component.subtitle, font: Font.regular(10.0), textColor: component.theme.chat.message.incoming.secondaryTextColor))
|
||||
text: .plain(NSAttributedString(string: component.subtitle, font: Font.with(size: 9.0, design: .round, weight: .bold), textColor: .white))
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: itemSize.width - 6.0, height: 100.0)
|
||||
)
|
||||
|
||||
let subtitleIconSize = self.subtitleIcon.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(BundleIconComponent(name: "Chat/Message/Subscriber", tintColor: .white)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: itemSize.width - 6.0, height: 100.0)
|
||||
)
|
||||
|
||||
let avatarSize = CGSize(width: 60.0, height: 60.0)
|
||||
let avatarFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((itemSize.width - avatarSize.width) / 2.0), y: 0.0), size: avatarSize)
|
||||
let titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((itemSize.width - titleSize.width) / 2.0), y: avatarFrame.maxY + 4.0), size: titleSize)
|
||||
let subtitleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((itemSize.width - subtitleSize.width) / 2.0), y: titleFrame.maxY + 1.0), size: subtitleSize)
|
||||
|
||||
let subtitleSpacing: CGFloat = 1.0 + UIScreenPixel
|
||||
let subtitleTotalWidth = subtitleIconSize.width + subtitleSize.width + subtitleSpacing
|
||||
let subtitleIconFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((itemSize.width - subtitleTotalWidth) / 2.0) + 1.0 - UIScreenPixel, y: avatarFrame.maxY - subtitleSize.height + 1.0 - UIScreenPixel), size: subtitleIconSize)
|
||||
let subtitleFrame = CGRect(origin: CGPoint(x: subtitleIconFrame.maxX + subtitleSpacing, y: avatarFrame.maxY - subtitleSize.height - UIScreenPixel), size: subtitleSize)
|
||||
|
||||
self.avatarNode.frame = avatarFrame
|
||||
self.avatarNode.setPeer(context: component.context, theme: component.theme, peer: component.peer)
|
||||
@ -607,18 +628,32 @@ private final class ChannelItemComponent: Component {
|
||||
if let titleView = self.title.view {
|
||||
if titleView.superview == nil {
|
||||
titleView.isUserInteractionEnabled = false
|
||||
self.containerButton.addSubview(titleView)
|
||||
self.addSubview(titleView)
|
||||
}
|
||||
titleView.frame = titleFrame
|
||||
}
|
||||
if let subtitleView = self.subtitle.view {
|
||||
if subtitleView.superview == nil {
|
||||
subtitleView.isUserInteractionEnabled = false
|
||||
self.containerButton.addSubview(subtitleView)
|
||||
self.addSubview(subtitleView)
|
||||
}
|
||||
subtitleView.frame = subtitleFrame
|
||||
}
|
||||
if let subtitleIconView = self.subtitleIcon.view {
|
||||
if subtitleIconView.superview == nil {
|
||||
subtitleIconView.isUserInteractionEnabled = false
|
||||
self.addSubview(subtitleIconView)
|
||||
}
|
||||
subtitleIconView.frame = subtitleIconFrame
|
||||
}
|
||||
|
||||
let strokeWidth: CGFloat = 1.0 + UIScreenPixel
|
||||
let avatarBadgeSize = CGSize(width: subtitleSize.width + 4.0 + 4.0 + 6.0, height: 15.0)
|
||||
self.avatarBadge.update(size: avatarBadgeSize, text: "", hasTimeoutIcon: false, useSolidColor: true, strokeColor: component.theme.chat.message.incoming.bubble.withoutWallpaper.fill.first!)
|
||||
|
||||
let avatarBadgeFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((avatarFrame.width - avatarBadgeSize.width) / 2.0), y: avatarFrame.height - avatarBadgeSize.height + 2.0), size: avatarBadgeSize).insetBy(dx: -strokeWidth, dy: -strokeWidth)
|
||||
self.avatarBadge.frame = avatarBadgeFrame
|
||||
|
||||
self.containerButton.frame = CGRect(origin: .zero, size: itemSize)
|
||||
|
||||
return itemSize
|
||||
|
Loading…
x
Reference in New Issue
Block a user