Best way to find the nearest UIView in Swift


December 2018


336 time


I am trying to find the nearest possible subview. In my app, there are 7 UIButtons and having a UILabels centered below with a margin of 20. It's not a subview.

What do I want? To find out the nearest label to a button with lesser efforts.

What am I doing currently? I am passing a UIButton in a function and calculating the possible distances from that button to all the labels. Then sorting the distances results in ascending and picking the first object to get the minimum distance and that should be my nearest label.

internal func findNearByLabel(withRespectToButton button: UIButton) -> UILabel? {

    var distance = Array<CGFloat>()
    var labels: Array<UILabel> = self.view.subViews(type: UILabel.self)

    labels.forEach { (label) in
        distance.append(self.distance(label.frame.origin, button.frame.origin))

    if !distance.isEmpty {
        let sortedDistance = Array.init(distance).sorted()
        let minDistance = sortedDistance.first!
        let indexOfMinDistance = distance.index(of: minDistance)!
        return labels[indexOfMinDistance]

    return nil

internal func distance(_ a: CGPoint, _ b: CGPoint) -> CGFloat {
    let xDistance = a.x - b.x
    let yDistance = a.y - b.y
    return CGFloat(sqrt((xDistance * xDistance) + (yDistance * yDistance)))

extension UIView {
    func subViews<T : UIView>(type : T.Type) -> [T]{
        var all = [T]()
        for view in self.subviews {
            if let aView = view as? T{
        return all

What is problem? No problem, it works, but I want to check if this is the best solution or there can be improvements possible?

1 answers


Just a few improvements using functional patterns.

// generic method, can be in a category
extension CGPoint {
    func distance(to point: CGPoint) -> CGFloat {
        // there is already a function for sqrt(x * x + y * y)
        return hypot(self.x - point.x, self.y - point.y)

internal func findNearByLabel(withRespectToButton button: UIButton) -> UILabel? {
    // you don't really need the subViews method
    let labels = self.view.subviews.flatMap { $0 as? UILabel }
    // don't forget we got "map"
    let distances = { $0.frame.origin.distance(to: button.frame.origin) }

    // we zip both sequences into one, that way we don't have to worry about sorting two arrays
    let labelDistances = zip(labels, distances)
    // we don't need sorting just to get the minimum
    let closestLabel = labelDistances.min { $0.1 < $1.1 }

    return closestLabel?.0