2

All I need to do is to create a CGRect that encompasses a UIBezierPath and I have an array of CGPoint.

To create this CGRect all I need to do is to enumerate the array of CGPoint and find the minimum and maximum X and Y coordinates.

Then I remembered that the old and good NSArray had this function called valueForKeyPath that could be used in conjunction with something like valueForKeyPath:@max.x or valueForKeyPath:@min.x that would do the magic to find the minimum and maximum values of x and y.

I am new to Swift. This is the first time I am using Swift to create serious code.

Is there any magic in Swift arrays that can be used to do that, instead of creating complex enumerations and loops?

Duck
  • 34,902
  • 47
  • 248
  • 470
  • 1
    You can use collection's max and/or min methods in Swift as well. Btw You should always use native collection when coding in Swift. Array is always preferred over NSArray. – Leo Dabus Mar 07 '19 at 22:29
  • ok, I know that I should not use NSArray in swift but remember that I have an array of CGPoint, so I cannot use min/max, I guess. – Duck Mar 07 '19 at 22:38
  • `if let maxX = points.max(by: { $0.x < $1.x }) {` `print(maxX)` `}` – Leo Dabus Mar 07 '19 at 22:48
  • 1
    @Rob yes, just straight lines. `bounds`... how could I miss that. Thanks. If you make this an answer I will accept that because it is the best answer. – Duck Mar 08 '19 at 00:26

2 Answers2

1

While you still have access to value(atKeyPath:) in Swift, there is a more idiomatic way to do this. I also happen to think it's clearer. Save the issue of optionals, of course…

let list = [CGPoint(x: 10, y: 20),
            CGPoint(x: 1, y: 2),
            CGPoint(x: 100, y: 200)]
let max = list.map { $0.x }.max()
let min = list.map { $0.x }.min()
print(max, min)

// Output: Optional(100.0) Optional(1.0)
Nick K9
  • 3,885
  • 1
  • 29
  • 62
1

The easy solution, if you already have the UIBezierPath, is to just get its bounds:

let rect = path.bounds

If you have an array of points and want the bounds of that encompasses those points, you could min and max the x and y coordinates, but I might reduce the union of all the points:

let points = [
    CGPoint(x: 200, y: 200),
    CGPoint(x: 300, y: 300),
    CGPoint(x: 200, y: 400),
    CGPoint(x: 100, y: 300)
]

let rect = points.first.map { firstPoint in
    points.dropFirst()
        .reduce(CGRect(origin: firstPoint, size: .zero)) { rect, point in
            rect.union(CGRect(origin: point, size: .zero))
    }
}
Rob
  • 415,655
  • 72
  • 787
  • 1,044