mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 06:35:51 +00:00
[ASCornerLayoutSpec] New layout spec class for declarative corner element layout. (#657)
* Add new layout spec class with snapshot testing. Update examples and CHANGELOG.md * Code review updates. * Open curly bracket in a new line.
This commit is contained in:
@@ -143,3 +143,140 @@ class FlexibleSeparatorSurroundingContent : LayoutExampleNode {
|
||||
return "try rotating me!"
|
||||
}
|
||||
}
|
||||
|
||||
class CornerLayoutSample : PhotoWithOutsetIconOverlay {
|
||||
let photoNode1 = ASImageNode()
|
||||
let photoNode2 = ASImageNode()
|
||||
let dotNode = ASImageNode()
|
||||
let badgeTextNode = ASTextNode()
|
||||
let badgeImageNode = ASImageNode()
|
||||
|
||||
struct ImageSize {
|
||||
static let avatar = CGSize(width: 100, height: 100)
|
||||
static let icon = CGSize(width: 26, height: 26)
|
||||
}
|
||||
|
||||
struct ImageColor {
|
||||
static let avatar = UIColor.lightGray
|
||||
static let icon = UIColor.red
|
||||
}
|
||||
|
||||
required init() {
|
||||
super.init()
|
||||
|
||||
let avatarImage = UIImage.draw(size: ImageSize.avatar, fillColor: ImageColor.avatar) { () -> UIBezierPath in
|
||||
return UIBezierPath(roundedRect: CGRect(origin: CGPoint.zero, size: ImageSize.avatar), cornerRadius: ImageSize.avatar.width / 20)
|
||||
}
|
||||
|
||||
let iconImage = UIImage.draw(size: ImageSize.icon, fillColor: ImageColor.icon) { () -> UIBezierPath in
|
||||
return UIBezierPath(ovalIn: CGRect(origin: CGPoint.zero, size: ImageSize.icon))
|
||||
}
|
||||
|
||||
photoNode1.image = avatarImage
|
||||
photoNode2.image = avatarImage
|
||||
dotNode.image = iconImage
|
||||
|
||||
badgeTextNode.attributedText = NSAttributedString.attributedString(string: " 999+ ", fontSize: 20, color: .white)
|
||||
|
||||
badgeImageNode.image = UIImage.as_resizableRoundedImage(withCornerRadius: 12, cornerColor: .clear, fill: .red)
|
||||
}
|
||||
|
||||
override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
|
||||
photoNode.style.preferredSize = ImageSize.avatar
|
||||
iconNode.style.preferredSize = ImageSize.icon
|
||||
|
||||
let badgeSpec = ASBackgroundLayoutSpec(child: badgeTextNode, background: badgeImageNode)
|
||||
let cornerSpec1 = ASCornerLayoutSpec(child: photoNode1, corner: dotNode, location: .topRight)
|
||||
let cornerSpec2 = ASCornerLayoutSpec(child: photoNode2, corner: badgeSpec, location: .topRight)
|
||||
let cornerSpec3 = ASCornerLayoutSpec(child: photoNode, corner: iconNode, location: .topRight)
|
||||
|
||||
cornerSpec1.offset = CGPoint(x: -3, y: 3)
|
||||
|
||||
let stackSpec = ASStackLayoutSpec.vertical()
|
||||
stackSpec.spacing = 40
|
||||
stackSpec.children = [cornerSpec1, cornerSpec2, cornerSpec3]
|
||||
|
||||
return stackSpec
|
||||
}
|
||||
|
||||
override class func title() -> String {
|
||||
return "Declarative way for Corner image Layout"
|
||||
}
|
||||
|
||||
override class func descriptionTitle() -> String? {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
class UserProfileSample : LayoutExampleNode {
|
||||
|
||||
let badgeNode = ASImageNode()
|
||||
let avatarNode = ASImageNode()
|
||||
let usernameNode = ASTextNode()
|
||||
let subtitleNode = ASTextNode()
|
||||
|
||||
struct ImageSize {
|
||||
static let avatar = CGSize(width: 44, height: 44)
|
||||
static let badge = CGSize(width: 15, height: 15)
|
||||
}
|
||||
|
||||
struct ImageColor {
|
||||
static let avatar = UIColor.lightGray
|
||||
static let badge = UIColor.red
|
||||
}
|
||||
|
||||
required init() {
|
||||
super.init()
|
||||
|
||||
avatarNode.image = UIImage.draw(size: ImageSize.avatar, fillColor: ImageColor.avatar) { () -> UIBezierPath in
|
||||
return UIBezierPath(ovalIn: CGRect(origin: CGPoint.zero, size: ImageSize.avatar))
|
||||
}
|
||||
|
||||
badgeNode.image = UIImage.draw(size: ImageSize.badge, fillColor: ImageColor.badge) { () -> UIBezierPath in
|
||||
return UIBezierPath(ovalIn: CGRect(origin: CGPoint.zero, size: ImageSize.badge))
|
||||
}
|
||||
|
||||
makeSingleLine(for: usernameNode, with: "Hello world", fontSize: 17, textColor: .black)
|
||||
makeSingleLine(for: subtitleNode, with: "This is a long long subtitle, with a long long appended string.", fontSize: 14, textColor: .lightGray)
|
||||
}
|
||||
|
||||
private func makeSingleLine(for node: ASTextNode, with text: String, fontSize: CGFloat, textColor: UIColor) {
|
||||
node.attributedText = NSAttributedString.attributedString(string: text, fontSize: fontSize, color: textColor)
|
||||
node.maximumNumberOfLines = 1
|
||||
}
|
||||
|
||||
override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
|
||||
let avatarBox = ASCornerLayoutSpec(child: avatarNode, corner: badgeNode, location: .bottomRight)
|
||||
avatarBox.offset = CGPoint(x: -6, y: -6)
|
||||
|
||||
let textBox = ASStackLayoutSpec.vertical()
|
||||
textBox.justifyContent = .spaceAround
|
||||
textBox.children = [usernameNode, subtitleNode]
|
||||
|
||||
let profileBox = ASStackLayoutSpec.horizontal()
|
||||
profileBox.spacing = 10
|
||||
profileBox.children = [avatarBox, textBox]
|
||||
|
||||
// Apply text truncation
|
||||
let elems: [ASLayoutElement] = [usernameNode, subtitleNode, textBox, profileBox]
|
||||
for elem in elems {
|
||||
elem.style.flexShrink = 1
|
||||
}
|
||||
|
||||
let insetBox = ASInsetLayoutSpec(
|
||||
insets: UIEdgeInsets(top: 120, left: 20, bottom: CGFloat.infinity, right: 20),
|
||||
child: profileBox
|
||||
)
|
||||
|
||||
return insetBox
|
||||
}
|
||||
|
||||
override class func title() -> String {
|
||||
return "Common user profile layout."
|
||||
}
|
||||
|
||||
override class func descriptionTitle() -> String? {
|
||||
return "For corner image layout and text truncation."
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,7 +26,9 @@ class OverviewViewController: ASViewController<ASTableNode> {
|
||||
HeaderWithRightAndLeftItems.self,
|
||||
PhotoWithInsetTextOverlay.self,
|
||||
PhotoWithOutsetIconOverlay.self,
|
||||
FlexibleSeparatorSurroundingContent.self
|
||||
FlexibleSeparatorSurroundingContent.self,
|
||||
CornerLayoutSample.self,
|
||||
UserProfileSample.self
|
||||
]
|
||||
|
||||
super.init(node: tableNode)
|
||||
|
||||
@@ -74,7 +74,21 @@ extension UIImage {
|
||||
|
||||
return roundedImage ?? self
|
||||
}
|
||||
|
||||
|
||||
class func draw(size: CGSize, fillColor: UIColor, shapeClosure: () -> UIBezierPath) -> UIImage {
|
||||
UIGraphicsBeginImageContext(size)
|
||||
|
||||
let path = shapeClosure()
|
||||
path.addClip()
|
||||
|
||||
fillColor.setFill()
|
||||
path.fill()
|
||||
|
||||
let image = UIGraphicsGetImageFromCurrentImageContext()
|
||||
UIGraphicsEndImageContext()
|
||||
|
||||
return image!
|
||||
}
|
||||
}
|
||||
|
||||
extension NSAttributedString {
|
||||
|
||||
Reference in New Issue
Block a user