Skip to content

Instantly share code, notes, and snippets.

@ryanlintott
Last active August 20, 2021 02:40
Show Gist options
  • Save ryanlintott/2340f35977bf2d1f7b6ea40aa379bcc6 to your computer and use it in GitHub Desktop.
Save ryanlintott/2340f35977bf2d1f7b6ea40aa379bcc6 to your computer and use it in GitHub Desktop.
Alternative Text view for SwiftUI with an extendable clipping area. This is a fix for certain fonts and characters that have a clipped intrinsic size.
import SwiftUI
import UIKit
struct NoClipText: UIViewRepresentable {
typealias UIViewType = NoClipLabel
let text: String
let font: UIFont
let clipExtension: EdgeSizes
func makeUIView(context: Context) -> UIViewType {
let uiView = UIViewType()
uiView.text = text
uiView.font = font
uiView.clipExtension = clipExtension
return uiView
}
func updateUIView(_ uiView: UIViewType, context: Context) {
uiView.text = text
uiView.font = font
uiView.clipExtension = clipExtension
}
}
struct NoClipText_Previews: PreviewProvider {
static let font: UIFont = UIFont(name: "Cochin", size: 48)!
static var previews: some View {
Group {
NoClipText(text: "Ǣʃ", font: font, clipExtension: .zero)
.fixedSize()
NoClipText(text: "Ǣʃ", font: font, clipExtension: .all(10))
.fixedSize()
}
.previewLayout(.sizeThatFits)
.border(Color.blue, width: 1)
.padding(10)
}
}
class NoClipLabel: UILabel {
static let defaultClipExtension: EdgeSizes = .all(10)
var clipExtension: EdgeSizes
var top: CGFloat { clipExtension.top }
var left: CGFloat { clipExtension.left }
var bottom: CGFloat { clipExtension.bottom }
var right: CGFloat { clipExtension.right }
var width: CGFloat { left + right }
var height: CGFloat { bottom + top }
required init(clipExtension: EdgeSizes = defaultClipExtension) {
self.clipExtension = clipExtension
super.init(frame: CGRect.zero)
}
override init(frame: CGRect) {
clipExtension = Self.defaultClipExtension
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
clipExtension = Self.defaultClipExtension
super.init(coder: aDecoder)
}
override func draw(_ rect: CGRect) {
super.drawText(in: rect.inset(by: UIEdgeInsets(top: top, left: left, bottom: bottom, right: right)))
}
override var alignmentRectInsets: UIEdgeInsets {
return .init(top: top, left: left, bottom: bottom, right: right)
}
override var intrinsicContentSize: CGSize {
var size = super.intrinsicContentSize
size.width += width
size.height += height
return size
}
override func sizeThatFits(_ size: CGSize) -> CGSize {
let fixedSize = CGSize(width: size.width - width, height: size.height - height)
let sizeWithoutExtension = super.sizeThatFits(fixedSize)
return CGSize(width: sizeWithoutExtension.width + width,
height: sizeWithoutExtension.height + height)
}
}
struct EdgeSizes: Equatable {
let top: CGFloat
let left: CGFloat
let bottom: CGFloat
let right: CGFloat
init(top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) {
self.top = top
self.left = left
self.bottom = bottom
self.right = right
}
init(vertical: CGFloat = 0, horizontal: CGFloat = 0) {
self.top = vertical
self.left = horizontal
self.bottom = vertical
self.right = horizontal
}
init(_ all: CGFloat) {
self.top = all
self.left = all
self.bottom = all
self.right = all
}
static let zero = EdgeSizes(0)
static func all(_ size: CGFloat) -> EdgeSizes {
EdgeSizes(size)
}
static func vertical(_ size: CGFloat) -> EdgeSizes {
EdgeSizes(vertical: size)
}
static func horizontal(_ size: CGFloat) -> EdgeSizes {
EdgeSizes(horizontal: size)
}
}
@ryanlintott
Copy link
Author

Modified from this solution on stackoverflow.

  • Added ability to customize all 4 edges
  • Added wrapper to create a SwiftUI view

@ryanlintott
Copy link
Author

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment