Skip to content

Instantly share code, notes, and snippets.

@haojianzong
Created June 28, 2025 08:26
Show Gist options
  • Save haojianzong/417758e36ff7530e2f1911892f983681 to your computer and use it in GitHub Desktop.
Save haojianzong/417758e36ff7530e2f1911892f983681 to your computer and use it in GitHub Desktop.
UIView+Pin.swift
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
import Foundation
import UIKit
// A small utility for NSLayoutDimension shortcuts, easily reduce lots of auto layout codes.
public protocol PinObject {
var topAnchor: NSLayoutYAxisAnchor { get }
var bottomAnchor: NSLayoutYAxisAnchor { get }
var leadingAnchor: NSLayoutXAxisAnchor { get }
var trailingAnchor: NSLayoutXAxisAnchor { get }
}
extension UIView: PinObject {}
extension UILayoutGuide: PinObject {}
public extension UIView {
struct PinOptions: OptionSet {
public let rawValue: Int
public static let all: PinOptions = [.top, .trailing, .bottom, .leading]
public static let top = PinOptions(rawValue: 1 << 0)
public static let trailing = PinOptions(rawValue: 1 << 1)
public static let bottom = PinOptions(rawValue: 1 << 2)
public static let leading = PinOptions(rawValue: 1 << 3)
public init(rawValue: Int) {
self.rawValue = rawValue
}
}
/// Add layout constraints. Also sets translatesAutoresizingMaskIntoConstraints to false.
///
/// - Parameters:
/// - pinObject: The pinObject that this view is pined to. Maybe a UIView or an UILayoutGuideObject
/// - edges: Choose which edge to pin
func pin(to pinObject: PinObject, edges: PinOptions, priority: UILayoutPriority) {
translatesAutoresizingMaskIntoConstraints = false
var constraints = [NSLayoutConstraint]()
if edges.contains(.leading) {
constraints.append(leadingAnchor.constraint(equalTo: pinObject.leadingAnchor))
}
if edges.contains(.trailing) {
constraints.append(trailingAnchor.constraint(equalTo: pinObject.trailingAnchor))
}
if edges.contains(.top) {
constraints.append(topAnchor.constraint(equalTo: pinObject.topAnchor))
}
if edges.contains(.bottom) {
constraints.append(bottomAnchor.constraint(equalTo: pinObject.bottomAnchor))
}
constraints.forEach { $0.priority = priority }
if !constraints.isEmpty {
NSLayoutConstraint.activate(constraints)
}
}
/// Pin to parentView edges
func pinEdges(to parentView: UIView, edges: PinOptions = .all, priority: UILayoutPriority = .required) {
pin(to: parentView, edges: edges, priority: priority)
}
/// Pin to parentView layoutMarginsGuide
func pinToMargins(of parentView: UIView, edges: PinOptions = .all, priority: UILayoutPriority = .required) {
pin(to: parentView.layoutMarginsGuide, edges: edges, priority: priority)
}
/// Pin to parentView safeAreaLayoutGuide
func pinToSafeArea(of parentView: UIView, edges: PinOptions = .all, priority: UILayoutPriority = .required) {
pin(to: parentView.safeAreaLayoutGuide, edges: edges, priority: priority)
}
func pinToEdges(of parentView: UIView, insets: UIEdgeInsets, edges: PinOptions = .all, priority: UILayoutPriority = .required) {
translatesAutoresizingMaskIntoConstraints = false
var constraints = [NSLayoutConstraint]()
if edges.contains(.leading) {
constraints.append(leadingAnchor.constraint(equalTo: parentView.leadingAnchor, constant: insets.left))
}
if edges.contains(.trailing) {
constraints.append(trailingAnchor.constraint(equalTo: parentView.trailingAnchor, constant: -insets.right))
}
if edges.contains(.top) {
constraints.append(topAnchor.constraint(equalTo: parentView.topAnchor, constant: insets.top))
}
if edges.contains(.bottom) {
constraints.append(bottomAnchor.constraint(equalTo: parentView.bottomAnchor, constant: -insets.bottom))
}
constraints.forEach { $0.priority = priority }
NSLayoutConstraint.activate(constraints)
}
}
public extension UILayoutPriority {
static var almostRequired: UILayoutPriority {
return .required - 1
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment