Skip to content

Commit

Permalink
feat(iOS): add support for non-uniform border radii
Browse files Browse the repository at this point in the history
Introduced the ability to set non-uniform border radii for iOS images.
This feature allows developers to specify different border radius values for different corner of a image, enhancing design flexibility.
  • Loading branch information
Florian Vogt committed May 25, 2024
1 parent 684e7a0 commit 0baf846
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 10 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,10 @@ await clearCache();
| cachePolicy | string | memory | The cache policy of the image |
| transitionDuration | number | 0.75 (iOS) Android (100) | The transition duration of the image |
| borderRadius | number | 0 | border radius of image |
| borderTopLeftRadius | number | 0 | top left border radius of image (Android only) |
| borderTopRightRadius | number | 0 | top right border radius of image (Android only) |
| borderBottomLeftRadius | number | 0 | bottom left border radius of image (Android only) |
| borderBottomRightRadius | number | 0 | bottom right border radius of image (Android only) |
| borderTopLeftRadius | number | 0 | top left border radius of image |
| borderTopRightRadius | number | 0 | top right border radius of image |
| borderBottomLeftRadius | number | 0 | bottom left border radius of image |
| borderBottomRightRadius | number | 0 | bottom right border radius of image |
| failureImage | string | | If the image fails to download this will be set (blurhash, thumbhash, base64) |
| progressiveLoadingEnabled | boolean | false | Progressively load images (iOS only) |
| onError | function | | The function to call when an error occurs. The error is passed as the first argument of the function |
Expand Down
2 changes: 1 addition & 1 deletion example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export default function App() {
borderTopLeftRadius:
Platform.OS === 'android' ? size : (size - 16) / 2,
borderBottomRightRadius:
Platform.OS === 'android' ? size : (size - 16) / 2,
Platform.OS === 'android' ? size / 1.5 : (size - 16) / 3,
cachePolicy: 'discWithCacheControl',
showActivityIndicator: true,
base64Placeholder:
Expand Down
94 changes: 89 additions & 5 deletions ios/FasterImageViewManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,27 @@ struct ImageOptions: Decodable {
let base64Placeholder: String?
let progressiveLoadingEnabled: Bool?
let borderRadius: Double?
let borderTopLeftRadius: Double?
let borderTopRightRadius: Double?
let borderBottomLeftRadius: Double?
let borderBottomRightRadius: Double?
let url: String
let headers: [String: String]?
let grayscale: Double?
}

struct BorderRadii {
var uniform: Double = 0.0
var topLeft: Double = 0.0
var topRight: Double = 0.0
var bottomLeft: Double = 0.0
var bottomRight: Double = 0.0

func sum() -> Double {
return uniform + topLeft + topRight + bottomLeft + bottomRight
}
}

/// A wrapper around `LazyImageView` to make it compatible with React Native.
final class FasterImageView: UIView {

Expand Down Expand Up @@ -88,11 +104,15 @@ final class FasterImageView: UIView {
if let base64Placeholder = options.base64Placeholder {
self.base64Placeholder = base64Placeholder
}
if let borderRadius = options.borderRadius {
lazyImageView.layer.cornerRadius = CGFloat(borderRadius)
lazyImageView.layer.masksToBounds = true
self.clipsToBounds = true
}

borderRadii = BorderRadii(
uniform: options.borderRadius ?? 0.0,
topLeft: options.borderTopLeftRadius ?? 0.0,
topRight: options.borderTopRightRadius ?? 0.0,
bottomLeft: options.borderBottomLeftRadius ?? 0.0,
bottomRight: options.borderBottomRightRadius ?? 0.0
)

if let blurhash = options.blurhash {
self.blurhash = blurhash
}
Expand Down Expand Up @@ -129,6 +149,70 @@ final class FasterImageView: UIView {
}
}

private func applyBorderRadii() {
let radiiSum = borderRadii.sum()

if radiiSum != 0.0 {
let nonUniformRadiiiSum = radiiSum - borderRadii.uniform

if nonUniformRadiiiSum == 0.0 || nonUniformRadiiiSum == borderRadii.uniform {
lazyImageView.layer.cornerRadius = CGFloat(borderRadii.uniform)
} else {
let path = UIBezierPath()
let bounds = lazyImageView.bounds

path.move(to: CGPoint(x: bounds.minX, y: bounds.minY + borderRadii.topLeft))
path.addArc(withCenter: CGPoint(x: bounds.minX + borderRadii.topLeft, y: bounds.minY + borderRadii.topLeft),
radius: borderRadii.topLeft,
startAngle: CGFloat.pi,
endAngle: 3 * CGFloat.pi / 2,
clockwise: true)

path.addLine(to: CGPoint(x: bounds.maxX - borderRadii.topRight, y: bounds.minY))
path.addArc(withCenter: CGPoint(x: bounds.maxX - borderRadii.topRight, y: bounds.minY + borderRadii.topRight),
radius: borderRadii.topRight,
startAngle: 3 * CGFloat.pi / 2,
endAngle: 0,
clockwise: true)

path.addLine(to: CGPoint(x: bounds.maxX, y: bounds.maxY - borderRadii.bottomRight))
path.addArc(withCenter: CGPoint(x: bounds.maxX - borderRadii.bottomRight, y: bounds.maxY - borderRadii.bottomRight),
radius: borderRadii.bottomRight,
startAngle: 0,
endAngle: CGFloat.pi / 2,
clockwise: true)

path.addLine(to: CGPoint(x: bounds.minX + borderRadii.bottomLeft, y: bounds.maxY))
path.addArc(withCenter: CGPoint(x: bounds.minX + borderRadii.bottomLeft, y: bounds.maxY - borderRadii.bottomLeft),
radius: borderRadii.bottomLeft,
startAngle: CGFloat.pi / 2,
endAngle: CGFloat.pi,
clockwise: true)

path.close()

let mask = CAShapeLayer()
mask.path = path.cgPath
lazyImageView.layer.mask = mask
}

lazyImageView.layer.masksToBounds = true
self.clipsToBounds = true
}
}

override func layoutSubviews() {
super.layoutSubviews()
applyBorderRadii()
}

var borderRadii = BorderRadii() {
didSet {
lazyImageView.setNeedsLayout()
applyBorderRadii()
}
}

var grayscale = 0.0 {
didSet {
if grayscale > 0 {
Expand Down

0 comments on commit 0baf846

Please sign in to comment.