Files
LCEssentials/Documentation/UIKit.md
2026-08-29 19:33:22 -03:00

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

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)

NSLayoutConstraint helpers (constraintWithMultiplier, matches(view:anchor:)) and [NSLayoutConstraint].filtered(view:anchor:) are internal — used by the UIView.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.01.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"

Collections & Tables

Media & Components