19 KiB
LCEssentials — UIKit
UIKit-era helpers: programmatic layout & constraints, view/control extensions, navigation, table/collection helpers, and drop-in components (image picker, image zoom, snackbar, GIF loading).
Foundation/value-type helpers are in Extensions.md; SwiftUI helpers in SwiftUI.md.
Every section is a collapsible block — click a heading to expand it.
Contents
- Layout & Constraints
- Views & Controls
- Navigation & Controllers
- Collections & Tables
- Media & Components
Layout & Constraints
UIView — programmatic constraints, chainable anchors
enum AnchorType
The anchor to pin when using setConstraintsTo. Cases: all, top, bottom,
leading, trailing, left, right, centerX, centerY, width, heigth
(sic), topToBottom, bottomToTop, leadingToTrailing, trailingToLeading,
plus …GreaterThanOrEqualTo / …LessThanOrEqualTo variants.
@discardableResult func setConstraintsTo(parentView: UIView, anchorType: AnchorType, value: CGFloat, safeArea: Bool = false) -> Self
Activate one constraint against parentView. Remembers parentView (in
viewReference) so follow-up calls can omit it. safeArea: true pins top/bottom
to the safe-area guide (iOS 11+).
card.addSubview(label)
label.setConstraintsTo(parentView: card, anchorType: .top, value: 12, safeArea: false)
.setConstraints(.leading, 16)
.setConstraints(.trailing, -16)
@discardableResult func setConstraintsTo(_ parentView:_ anchorType:_ value:_ safeArea: Bool = false) -> Self
Positional-argument shorthand for the above.
@discardableResult func setConstraintsTo(anchorType: AnchorType, value: CGFloat, safeArea: Bool = false) -> UIView
Reuse the last parentView (viewReference). Traps with fatalError if no
parentView was set first.
@discardableResult func setConstraints(_ anchorType: AnchorType, _ value: CGFloat, _ safeArea: Bool = false) -> UIView
Positional shorthand for the reuse form — the one you chain.
func setConstraints(_ toScrollView: UIScrollView, direction: UICollectionView.ScrollDirection = .vertical)
Pin self as the single content view of a scroll view (edges + matching
width/height, with a low-priority constraint on the scroll axis). Traps if
self is itself a UIScrollView.
scrollView.addSubview(content)
content.setConstraints(scrollView, direction: .vertical)
Constraint accessors
widthConstraint, heightConstraint, leadingConstraint, trailingConstraint,
topConstraint, bottomConstraint, centerXConstraints, centerYConstraints —
the first matching NSLayoutConstraint found by walking up the hierarchy.
box.widthConstraint?.constant = 120
UIView.animate(withDuration: 0.2) { self.view.layoutIfNeeded() }
func findConstraint(attribute: NSLayoutConstraint.Attribute, for view: UIView) -> NSLayoutConstraint?
Search self and ancestors for a constraint on attribute involving view.
func constraints(on anchor:) -> [NSLayoutConstraint]
Constraints touching a given NSLayoutYAxisAnchor / XAxisAnchor / Dimension of self.
@discardableResult func setHeight(size:) -> Self / setHeight(min:) / setWidth(size:) / setWidth(min:)
Activate a fixed or minimum dimension constraint.
avatar.setWidth(size: 44).setHeight(size: 44)
func setWidth(_ toView: UIView? = nil, constant: CGFloat, _ multiplier: CGFloat = 0) -> Self / setHeight(...)
Dimension relative to another view (multiplier) or fixed (multiplier == 0).
Frame setters
setX(x:), setY(y:), setFrameWidth(width:), setFrameHeight(height:) — mutate
self.frame directly (manual layout, not Auto Layout).
UIView — hierarchy, appearance, effects
static var className: String
String(describing: self).
UIButton.className // "UIButton"
func addSubview(_ subview:, translatesAutoresizingMaskIntoConstraints: Bool = false) / addSubviews(_ subviews: [UIView], …)
Add a view (or many) and set translatesAutoresizingMaskIntoConstraints in one call.
view.addSubviews([header, body, footer]) // all ready for Auto Layout
func subviews<T>(ofType _: T.Type) -> [T]
All descendants of a type, recursively.
formView.subviews(ofType: UITextField.self)
func findAView<T>(_ ofType: T.Type) -> T?
First descendant of a type.
var parentViewController: UIViewController?
Nearest owning view controller (walks the responder chain).
var borderColor: UIColor? / var borderWidth: CGFloat / var cornerRadius: CGFloat
Layer border/corner shortcuts (cornerRadius also sets masksToBounds).
card.cornerRadius = 12
card.borderWidth = 1
card.borderColor = .separator
func setRadius(top: Bool, bottom: Bool, radius: CGFloat = 8)
Round only the top and/or bottom corners.
sheet.setRadius(top: true, bottom: false, radius: 16)
func applyShadow(color:offSet:radius:opacity:shouldRasterize: Bool = true, rasterizationScaleTo: = UIScreen.main.scale) / func removeShadow()
Layer shadow with rasterisation on by default.
card.applyShadow(color: .black, offSet: CGSize(width: 0, height: 2),
radius: 6, opacity: 0.15)
func insertBlurView(style: UIBlurEffect.Style, color: UIColor = .black, alpha: CGFloat = 0.9)
Insert a full-bleed UIVisualEffectView behind the content.
func drawCircle(inCoord x:y:with radius:strokeColor: = .red, fillColor: = .gray, isEmpty: Bool = false) -> [String: Any]
Add a circular CAShapeLayer; returns ["path": UIBezierPath, "layer": CAShapeLayer].
var isRightToLeft: Bool
Effective RTL layout direction.
var screenshot: UIImage? / func asImage() -> UIImage
Render the view to an image (screenshot via the legacy context, asImage via UIGraphicsImageRenderer).
let png = chartView.asImage().pngData()
var globalPoint: CGPoint? / var globalFrame: CGRect? / var absolutePosition: CGRect
Origin/frame converted to window coordinates.
func fadeIn(withDuration: TimeInterval = 1, withDelay: TimeInterval = 0, completionHandler: @escaping (Bool) -> Void) / func fadeOut(...)
Animate alpha to 1 / 0. (Completion-handler API — pre-dates async/await.)
overlay.fadeOut { _ in overlay.removeFromSuperview() }
var viewReference: UIView?
Scratch reference used internally by the chained setConstraintsTo calls.
GradientOrientation / EnumBorderSide
enum GradientOrientation
topRightBottomLeft, topLeftBottomRight, horizontal, vertical — maps to a
CAGradientLayer start/end point pair. Passed to gradient helpers elsewhere in
the package.
enum EnumBorderSide
top, bottom, left, right — which edge to draw a single-side border on.
UIStackView — convenience init, bulk arranged-subview ops (iOS 9+)
convenience init(arrangedSubviews: [UIView]? = nil, axis: = .vertical, spacing: = 0, alignment: = .fill, distribution: = .fill, layoutMargins: = .zero, isMarginsRelative: Bool = true)
One-call configured stack view.
let stack = UIStackView(arrangedSubviews: [title, subtitle],
axis: .vertical, spacing: 4)
func addArrangedSubviews(_ views: [UIView], translateAutoresizing: Bool = false)
Add many arranged subviews, setting their autoresizing flag.
func removeAllArrangedSubviews(deactivateConstraints: Bool = true)
Remove and dispose every arranged subview.
func removeSubview(view: UIView, deactivateConstraints: Bool = true)
Remove one arranged subview (optionally deactivating its constraints and removing from the hierarchy).
func addSpace(_ size: CGFloat, backgroundColor: UIColor = .clear)
Insert a fixed-size spacer view sized along the stack's axis.
stack.addArrangedSubviews([row1, row2])
stack.addSpace(24)
stack.addArrangedSubview(row3)
NSLayoutConstrainthelpers (constraintWithMultiplier,matches(view:anchor:)) and[NSLayoutConstraint].filtered(view:anchor:)areinternal— used by theUIView.constraints(on:)accessors above, not called directly.
Views & Controls
UIButton — per-state accessors, all-state setters
Per-state properties
imageForNormal / imageForHighlighted / imageForSelected / imageForDisabled,
titleForNormal / …Highlighted / …Selected / …Disabled,
titleColorForNormal / …Highlighted / …Selected / …Disabled — get/set
shortcuts for the matching UIControl.State (also @IBInspectable).
button.titleForNormal = "Save"
button.titleColorForDisabled = .tertiaryLabel
func setTitleForAllStates(_:) / func setTitleColorForAllStates(_:) / func setImageForAllStates(_:)
Apply one value to .normal, .selected, .highlighted, .disabled at once.
button.setTitleColorForAllStates(.white)
func centerTextAndImage(spacing: CGFloat)
Balance title/image edge insets so text + icon sit centred with spacing between them.
button.centerTextAndImage(spacing: 8)
UILabel
func lineNumbers() -> Int
Rendered line count at the current width.
if bodyLabel.lineNumbers() > 3 { showMoreButton() }
var getEstimatedHeight: CGFloat
Height the label would need to show its full text/attributed text unclipped.
UITextField
var placeholderColor: UIColor
Get/set the placeholder text colour (rebuilds attributedPlaceholder; setter is a
no-op if no placeholder text is set).
field.placeholder = "Email"
field.placeholderColor = .secondaryLabel
func addPaddingLeft(_ padding: CGFloat)
Inset the text from the left with an empty spacer view.
func addPaddingLeftIcon(_ image: UIImage, padding: CGFloat)
Left view = an icon plus trailing padding.
field.addPaddingLeftIcon(UIImage(systemName: "magnifyingglass")!, padding: 8)
UIImageView (@MainActor)
func changeColorOfImage(_ color: UIColor, image: UIImage?) -> UIImageView
Set a template-rendered image tinted to color; returns self.
iconView.changeColorOfImage(.systemBlue, image: UIImage(named: "star"))
var encodeToBase64: String?
JPEG (quality 0.6) of the current image as a Base64 string.
func addAspectRatioConstraint() / func removeAspectRatioConstraint()
Add / remove a width-to-height constraint matching the current image's ratio.
photoView.image = photo
photoView.addAspectRatioConstraint()
UIImage — recolour, resize, thumbnails, masks, init
func imageWithColor(color: UIColor) -> UIImage / func tintImage(color: UIColor) -> UIImage
Return a copy filled / tinted with color (keeps the alpha shape).
let redIcon = icon.tintImage(color: .systemRed)
func backgroundColorTransparent(initialColor: UIColor, finalColor: UIColor) -> UIImage?
Make pixels in a colour range transparent.
class func outlinedEllipse(size: CGSize, color: UIColor, lineWidth: CGFloat = 1) -> UIImage?
Generate a stroked-ellipse image.
UIImage.outlinedEllipse(size: CGSize(width: 24, height: 24), color: .label)
func resizeImage(newWidth: CGFloat) -> UIImage
Scale to newWidth, keeping the aspect ratio.
func createThumbnail(_ maxPixelSize: UInt) -> UIImage
Fast down-sampled thumbnail via CGImageSource.
let thumb = fullImage.createThumbnail(200)
func maskWithAlphaImage(maskImage: UIImage) -> UIImage
Use another image's alpha as a mask.
func isAnimated() -> Bool
Whether the image has more than one frame.
init?(base64String: String, scale: CGFloat = 1)
Decode a Base64 string to an image.
init(view: UIView) — @MainActor
Rasterise a view into an image.
let snapshot = UIImage(view: cardView)
UIColor — hex, components
convenience init(hex: String)
Parse #RGB, #RGBA, #RRGGBB, or #RRGGBBAA (with or without #).
view.backgroundColor = UIColor(hex: "#1E88E5")
UIColor(hex: "FF0000CC") // red at 80% alpha
var hexString: String?
#RRGGBB (or #RRGGBBAA when alpha < 1).
var redValue / var greenValue / var blueValue / var alphaValue
Individual channel values (CGFloat, via CIColor).
UIColor.systemBlue.redValue // 0.0…1.0
UIScrollView — snapshot, visible rect, paged scrolling
var snapshot: UIImage?
Image of the entire content size (not just the visible part) — works on
UITableView / UICollectionView too.
var visibleRect: CGRect
The currently visible content region.
var offsetInPage: CGFloat
Fractional position within the current page height (0.0…1.0).
func scrollUp(animated:) / scrollDown(animated:) / scrollLeft(animated:) / scrollRight(animated:)
Move one page (respects isPagingEnabled). animated defaults to true.
nextButton.onTap = { scrollView.scrollRight() }
enum orientation
horizontal / vertical — helper enum used by scroll utilities.
UITapGestureRecognizer
func didTapAttributedTextInLabel(label: UILabel, textToTouch: String) -> Bool
Whether the tap landed on a given substring of a label's attributed text — for making part of a label tappable.
@objc func handleTap(_ g: UITapGestureRecognizer) {
if g.didTapAttributedTextInLabel(label: termsLabel, textToTouch: "Terms of Use") {
openTerms()
}
}
Navigation & Controllers
UINavigationController — completion-handler push/pop, transparent bar
func pushViewController(_:animated: Bool = true, completion: (() -> Void)? = nil)
func popViewController(animated: Bool = true, _ completion: (() -> Void)? = nil)
func popToViewController(_:animated: Bool = true, _ completion: (() -> Void)? = nil)
The standard transitions with a completion block (wrapped in a CATransaction).
navigationController?.pushViewController(detail) {
print("detail is on screen")
}
func pushViewController(_:hidesBottomBar: Bool = false, animated: Bool = true) — not tvOS
Push while setting hidesBottomBarWhenPushed.
func makeTransparent(withTint tint: UIColor = .white)
Clear background + shadow, translucent, tinted bar/title.
navigationController?.makeTransparent(withTint: .label)
UIViewController — state, instantiation, dismissal, toasts, keyboard, observers
var isVisible: Bool / var isLoaded: Bool
View is loaded and in a window. (isLoaded is an alias.)
var isModal: Bool
Whether the controller is presented modally (vs. pushed).
static var className: String / static var identifier: String / static var segueID: String
"MyVC", "idMyVC", "idSegueMyVC".
static func instantiate<T>(storyBoard: String, identifier: String? = nil, bundle: Bundle? = …) -> T
Load a controller from a storyboard by (default) T.identifier.
let vc: ProfileVC = ProfileVC.instantiate(storyBoard: "Main")
static func instatiate<T>(nibName: String, bundle: Bundle? = nil) -> T (sic — "instatiate")
Load from a nib and force an initial layout pass.
func present(viewControllerToPresent:completion: @escaping () -> Void)
present(_:animated:) with a completion block.
func closeController(jumpToController: UIViewController? = nil, completion: @escaping () -> Void)
Dismiss if modal, else pop (to jumpToController if given), then call completion.
closeController { self.refreshList() }
func show(toastWith message:, font: = system 12, toastPosition: ToastPosition, backgroundColor: = .black, textColor: = .white, duration: = 3)
Show a temporary rounded toast label. ToastPosition is .top / .down
(notch-aware at the top).
show(toastWith: "Saved", toastPosition: .down)
func addNotificationObserver(name:selector:) / func removeNotificationObserver(name:) / func removeNotificationsObserver()
NotificationCenter registration shortcuts (last one removes all).
@objc func dismissSystemKeyboard(_ sender: UITapGestureRecognizer)
Ready-made selector for a tap-to-dismiss-keyboard gesture.
view.addGestureRecognizer(UITapGestureRecognizer(target: self,
action: #selector(dismissSystemKeyboard(_:))))
UITabBarController — badges, animated tab switching
func setBadges(badgeValues: [Int], font: UIFont = Helvetica-Light 11)
Set numeric badges for every tab at once (0 = no badge). Custom-drawn
(CustomTabBadge label), so they position above each tab item.
tabBarController?.setBadges(badgeValues: [0, 3, 0, 12])
func addBadge(index:value:color:font:)
Add a single custom badge.
func setSelectedView(atIndex:withAnimation: Bool = false, completion:)
Select a tab (pops that tab's nav stack to root first).
func setSelectedView(withNoPop atIndex:withAnimation: Bool = false, completion:)
Same, without popping to root.
func animateToTab(toIndex: Int)
Slide-transition between tabs.
func changeViewControllerToItem(withViewController:Item:)
Replace a tab's root controller then switch to that tab.
tabBarController?.changeViewControllerToItem(withViewController: NewHome(), Item: 0)
class CustomTabBadge: UILabel
The badge label type used above (init(font:)).
UIApplication — environment, app info, open URL
enum Environment + static var inferredEnvironment: Environment
.debug / .testFlight / .appStore, inferred from build config, simulator, and
the provisioning/receipt files.
if UIApplication.inferredEnvironment == .appStore { enableAnalytics() }
static var displayName: String? / static var buildNumber: String? / static var version: String?
Bundle info values. (LCEssentials.appVersion etc. forward to these.)
static func openURL(urlStr: String)
Open a URL string if it can be opened.
UIApplication.openURL(urlStr: "https://loverde.com.br")
UIResponder
var getParentViewController: UIViewController?
Walk the responder chain to the owning view controller.
someView.getParentViewController?.present(alert, animated: true)
UIDevice — notch metrics, model name
static var topNotch: CGFloat / static var bottomNotch: CGFloat / static var hasNotch: Bool
Safe-area top/bottom insets and whether the device has a notch / Dynamic Island.
let headerY: CGFloat = UIDevice.hasNotch ? 44 : 20
var modelName: String
Marketing name from the hardware identifier ("iPhone 15 Pro", "iPad Air (5th generation)", …); falls back to the raw identifier for unknown devices. Reads SIMULATOR_MODEL_IDENTIFIER on the simulator.
UIDevice.current.modelName // "iPhone 16 Pro"