Best way to find the nearest UIView in Swift

Refresh

December 2018

Views

336 time

1

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{
                all.append(aView)
            }
        }
        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

3

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 = labels.map { $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
}