Skip to content
This repository was archived by the owner on Jun 4, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion WordPressUI.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "WordPressUI"
s.version = "1.3.4"
s.version = "1.3.5-beta.1"
s.summary = "Home of reusable WordPress UI components."

s.description = <<-DESC
Expand Down
4 changes: 4 additions & 0 deletions WordPressUI.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
02CCC587230121440051D40B /* UIView+AutoLayoutHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02CCC586230121440051D40B /* UIView+AutoLayoutHelperTests.swift */; };
17576E6320AC7A28008612EF /* GradientView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17576E6220AC7A28008612EF /* GradientView.swift */; };
1A40951C2271B3C4009AA86D /* NSBundle+ResourceBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A40951B2271B3C4009AA86D /* NSBundle+ResourceBundle.swift */; };
43067E2B203C8CC4001DD610 /* UIControl+BlockEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43067E2A203C8CC4001DD610 /* UIControl+BlockEvents.swift */; };
Expand Down Expand Up @@ -86,6 +87,7 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
02CCC586230121440051D40B /* UIView+AutoLayoutHelperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+AutoLayoutHelperTests.swift"; sourceTree = "<group>"; };
17576E6220AC7A28008612EF /* GradientView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GradientView.swift; sourceTree = "<group>"; };
1A40951B2271B3C4009AA86D /* NSBundle+ResourceBundle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSBundle+ResourceBundle.swift"; sourceTree = "<group>"; };
43067E2A203C8CC4001DD610 /* UIControl+BlockEvents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIControl+BlockEvents.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -211,6 +213,7 @@
B5226C67207CCDB2003C606E /* GravatarTest.swift */,
B529F288202C855B00895D88 /* UIColorHelpersTests.swift */,
577FC78622985DD0005BA78F /* UIView+ChangeLayoutMarginsTests.swift */,
02CCC586230121440051D40B /* UIView+AutoLayoutHelperTests.swift */,
);
path = Extensions;
sourceTree = "<group>";
Expand Down Expand Up @@ -566,6 +569,7 @@
files = (
577FC78722985DD0005BA78F /* UIView+ChangeLayoutMarginsTests.swift in Sources */,
9A6EC88D21DA4832007815FF /* UIViewControllerHelperTest.swift in Sources */,
02CCC587230121440051D40B /* UIView+AutoLayoutHelperTests.swift in Sources */,
B529F289202C855B00895D88 /* UIColorHelpersTests.swift in Sources */,
57BC0C6D228DF1E000C1F070 /* UIView+GhostTests.swift in Sources */,
57BC0C6F228DF35400C1F070 /* UITableView+GhostTests.swift in Sources */,
Expand Down
44 changes: 40 additions & 4 deletions WordPressUI/Extensions/UIView+Helpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,24 @@ extension UIView {
addConstraints(newConstraints)
}

/// Adds constraints that pin a subview to self with zero insets.
///
/// - Parameter subview: a subview to be pinned to self.
@objc public func pinSubviewToAllEdges(_ subview: UIView) {
pinSubviewToAllEdges(subview, insets: .zero)
}

/// Adds constraints that pin a subview to self with padding insets.
///
/// - Parameters:
/// - subview: a subview to be pinned to self.
/// - insets: spacing between each subview edge to self. A positive value for an edge indicates that the subview is inside self on that edge.
@objc public func pinSubviewToAllEdges(_ subview: UIView, insets: UIEdgeInsets) {
NSLayoutConstraint.activate([
leadingAnchor.constraint(equalTo: subview.leadingAnchor),
trailingAnchor.constraint(equalTo: subview.trailingAnchor),
topAnchor.constraint(equalTo: subview.topAnchor),
bottomAnchor.constraint(equalTo: subview.bottomAnchor),
leadingAnchor.constraint(equalTo: subview.leadingAnchor, constant: -insets.left),
trailingAnchor.constraint(equalTo: subview.trailingAnchor, constant: insets.right),
topAnchor.constraint(equalTo: subview.topAnchor, constant: -insets.top),
bottomAnchor.constraint(equalTo: subview.bottomAnchor, constant: insets.bottom),
])
}

Expand All @@ -32,6 +44,30 @@ extension UIView {
])
}

/// Adds constraints that pin a subview to self's safe area with padding insets.
///
/// - Parameters:
/// - subview: a subview to be pinned to self's safe area.
@objc public func pinSubviewToSafeArea(_ subview: UIView) {
pinSubviewToSafeArea(subview, insets: .zero)
}

/// Adds constraints that pin a subview to self's safe area with padding insets.
///
/// - Parameters:
/// - subview: a subview to be pinned to self's safe area.
/// - insets: spacing between each subview edge to self's safe area. A positive value for an edge indicates that the subview is inside safe area on that edge.
@objc public func pinSubviewToSafeArea(_ subview: UIView, insets: UIEdgeInsets) {
if #available(iOS 11.0, *) {
NSLayoutConstraint.activate([
safeAreaLayoutGuide.leadingAnchor.constraint(equalTo: subview.leadingAnchor, constant: -insets.left),
safeAreaLayoutGuide.trailingAnchor.constraint(equalTo: subview.trailingAnchor, constant: insets.right),
safeAreaLayoutGuide.topAnchor.constraint(equalTo: subview.topAnchor, constant: -insets.top),
safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: subview.bottomAnchor, constant: insets.bottom),
])
}
}

@objc public func findFirstResponder() -> UIView? {
if isFirstResponder {
return self
Expand Down
122 changes: 122 additions & 0 deletions WordPressUITests/Extensions/UIView+AutoLayoutHelperTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import XCTest

@testable import WordPressUI

class UIViewAutoLayoutHelperTests: XCTestCase {
private var view: UIView!
private var subview: UIView!

override func setUp() {
super.setUp()
view = UIView(frame: .zero)
subview = UIView(frame: .zero)
}

// MARK: tests for `pinSubviewToAllEdges`

func testPinSubviewToAllEdgesWithZeroInsets() {
view.addSubview(subview)
view.pinSubviewToAllEdges(subview)

let topConstraint = getConstraint(from: view,
filter: { $0.firstAnchor == view.topAnchor && $0.secondAnchor == subview.topAnchor })
XCTAssertEqual(topConstraint.constant, 0)

let leadingConstraint = getConstraint(from: view,
filter: { $0.firstAnchor == view.leadingAnchor && $0.secondAnchor == subview.leadingAnchor })
XCTAssertEqual(leadingConstraint.constant, 0)

let trailingConstraint = getConstraint(from: view,
filter: { $0.firstAnchor == view.trailingAnchor && $0.secondAnchor == subview.trailingAnchor })
XCTAssertEqual(trailingConstraint.constant, 0)

let bottomConstraint = getConstraint(from: view,
filter: { $0.firstAnchor == view.bottomAnchor && $0.secondAnchor == subview.bottomAnchor })
XCTAssertEqual(bottomConstraint.constant, 0)
XCTAssertEqual(bottomConstraint.secondAnchor, subview.bottomAnchor)
}

func testPinSubviewToAllEdgesWithNonZeroInsets() {
view.addSubview(subview)
let insets = UIEdgeInsets(top: 10, left: 12, bottom: 17, right: 25)
view.pinSubviewToAllEdges(subview, insets: insets)

// Self.top = subview.top - insets.top
let topConstraint = getConstraint(from: view,
filter: { $0.firstAnchor == view.topAnchor && $0.secondAnchor == subview.topAnchor })
XCTAssertEqual(topConstraint.constant, -insets.top)

// Self.leading = subview.leading - insets.left
let leadingConstraint = getConstraint(from: view,
filter: { $0.firstAnchor == view.leadingAnchor && $0.secondAnchor == subview.leadingAnchor })
XCTAssertEqual(leadingConstraint.constant, -insets.left)

// Self.trailing = subview.trailing + insets.right
let trailingConstraint = getConstraint(from: view,
filter: { $0.firstAnchor == view.trailingAnchor && $0.secondAnchor == subview.trailingAnchor })
XCTAssertEqual(trailingConstraint.constant, insets.right)

// Self.bottom = subview.bottom + insets.bottom
let bottomConstraint = getConstraint(from: view,
filter: { $0.firstAnchor == view.bottomAnchor && $0.secondAnchor == subview.bottomAnchor })
XCTAssertEqual(bottomConstraint.constant, insets.bottom)
}

// MARK: tests for `pinSubviewToSafeArea`

func testPinSubviewToSafeAreaWithZeroInsets() {
view.addSubview(subview)
view.pinSubviewToSafeArea(subview)

let topConstraint = getConstraint(from: view,
filter: { $0.firstAnchor == view.safeAreaLayoutGuide.topAnchor && $0.secondAnchor == subview.topAnchor })
XCTAssertEqual(topConstraint.constant, 0)

let leadingConstraint = getConstraint(from: view,
filter: { $0.firstAnchor == view.safeAreaLayoutGuide.leadingAnchor && $0.secondAnchor == subview.leadingAnchor })
XCTAssertEqual(leadingConstraint.constant, 0)

let trailingConstraint = getConstraint(from: view,
filter: { $0.firstAnchor == view.safeAreaLayoutGuide.trailingAnchor && $0.secondAnchor == subview.trailingAnchor })
XCTAssertEqual(trailingConstraint.constant, 0)

let bottomConstraint = getConstraint(from: view,
filter: { $0.firstAnchor == view.safeAreaLayoutGuide.bottomAnchor && $0.secondAnchor == subview.bottomAnchor })
XCTAssertEqual(bottomConstraint.constant, 0)
}

func testPinSubviewToSafeAreaWithNonZeroInsets() {
view.addSubview(subview)
let insets = UIEdgeInsets(top: 10, left: 12, bottom: 17, right: 25)
view.pinSubviewToSafeArea(subview, insets: insets)

// Self safe area.top = subview.top - insets.top
let topConstraint = getConstraint(from: view,
filter: { $0.firstAnchor == view.safeAreaLayoutGuide.topAnchor && $0.secondAnchor == subview.topAnchor })
XCTAssertEqual(topConstraint.constant, -insets.top)

// Self safe area.leading = subview.leading - insets.left
let leadingConstraint = getConstraint(from: view,
filter: { $0.firstAnchor == view.safeAreaLayoutGuide.leadingAnchor && $0.secondAnchor == subview.leadingAnchor })
XCTAssertEqual(leadingConstraint.constant, -insets.left)

// Self safe area.trailing = subview.trailing + insets.right
let trailingConstraint = getConstraint(from: view,
filter: { $0.firstAnchor == view.safeAreaLayoutGuide.trailingAnchor && $0.secondAnchor == subview.trailingAnchor })
XCTAssertEqual(trailingConstraint.constant, insets.right)

// Self safe area.bottom = subview.bottom + insets.bottom
let bottomConstraint = getConstraint(from: view,
filter: { $0.firstAnchor == view.safeAreaLayoutGuide.bottomAnchor && $0.secondAnchor == subview.bottomAnchor })
XCTAssertEqual(bottomConstraint.constant, insets.bottom)
}

private func getConstraint(from view: UIView, filter: (NSLayoutConstraint) -> Bool) -> NSLayoutConstraint {
let constraints = view.constraints.filter(filter)
guard let constraint = constraints.first, constraints.count == 1 else {
XCTFail("Exactly one constraint corresponding to the given filter should have been created")
fatalError()
}
return constraint
}
}