Skip to content

Instantly share code, notes, and snippets.

@yesleon
Last active June 4, 2018 22:49
Show Gist options
  • Save yesleon/e6ff59838628a9c19dc3a1492f4b7a9e to your computer and use it in GitHub Desktop.
Save yesleon/e6ff59838628a9c19dc3a1492f4b7a9e to your computer and use it in GitHub Desktop.
Add a placeholder property to UITextView.
//
// UITextView+placeholder.swift
//
// Created by Li-Heng Hsu on 02/06/2018.
// Copyright © 2018 narrativesaw. All rights reserved.
//
import UIKit
private let defaultPlaceholderColor = UIColor.gray
extension UITextView {
private var placeholderLabel: PlaceholderLabel {
if let label = subviews.compactMap( { $0 as? PlaceholderLabel }).first {
return label
} else {
let label = PlaceholderLabel(frame: bounds)
label.autoresizingMask = [.flexibleBottomMargin, .flexibleWidth]
label.font = font
label.textColor = defaultPlaceholderColor
label.numberOfLines = 0
label.insetsProvider = self
textStorage.delegate = label
insertSubview(label, at: 0)
return label
}
}
@IBInspectable
var placeholder: String {
get {
return placeholderLabel.text ?? ""
}
set {
placeholderLabel.text = newValue
}
}
@IBInspectable
var placeholderColor: UIColor {
get {
return placeholderLabel.textColor
}
set {
placeholderLabel.textColor = newValue
}
}
}
extension UITextView: PlaceholderLabelInsetsProviding {
fileprivate func insets(for label: PlaceholderLabel) -> UIEdgeInsets {
let padding = textContainer.lineFragmentPadding
var insets = textContainerInset
insets.left += padding
insets.right += padding
return insets
}
}
private protocol PlaceholderLabelInsetsProviding: AnyObject {
func insets(for label: PlaceholderLabel) -> UIEdgeInsets
}
private class PlaceholderLabel: UILabel {
weak var insetsProvider: PlaceholderLabelInsetsProviding?
override func drawText(in rect: CGRect) {
var rect = rect
if let insets = insetsProvider?.insets(for: self) {
rect = UIEdgeInsetsInsetRect(rect, insets)
}
rect.size = sizeThatFits(CGSize(width: rect.width, height: .greatestFiniteMagnitude))
super.drawText(in: rect)
}
}
extension PlaceholderLabel: NSTextStorageDelegate {
func textStorage(_ textStorage: NSTextStorage, didProcessEditing editedMask: NSTextStorageEditActions, range editedRange: NSRange, changeInLength delta: Int) {
if editedMask.contains(.editedCharacters) {
isHidden = textStorage.length != 0
}
}
}
@yesleon
Copy link
Author

yesleon commented Jun 4, 2018

I fixed the auto resizing part and moved the nested class out to make the code structure flatter.

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