diff --git a/Example/Pods/Pods.xcodeproj/project.pbxproj b/Example/Pods/Pods.xcodeproj/project.pbxproj index e04e56f..38ecb5e 100644 --- a/Example/Pods/Pods.xcodeproj/project.pbxproj +++ b/Example/Pods/Pods.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 620CE73B15C504B32DB819EAD5979CCB /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 384DDA2CB25005BD6479B5987C619DD4 /* Foundation.framework */; }; 655262048678A7D25BFF931ACB3B32C2 /* Pods-TSAlertController_Example-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 0276C3F2BA9F25C18E5983826ED58A78 /* Pods-TSAlertController_Example-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; B28E065F8186E15FB50B6583D8F5D387 /* TSAlertController-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 72485BDD7C643F0AAC9F1C24B82D07CB /* TSAlertController-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C700A0A02D8C716F00D5EFD8 /* UIBezierPath+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C700A09F2D8C716F00D5EFD8 /* UIBezierPath+Extension.swift */; }; C79BCEAB2D5D6F0000213363 /* SlideUpAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C79BCE8B2D5D6F0000213363 /* SlideUpAnimator.swift */; }; C79BCEAC2D5D6F0000213363 /* TSAlertController+Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C79BCE972D5D6F0000213363 /* TSAlertController+Configuration.swift */; }; C79BCEAD2D5D6F0000213363 /* UIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C79BCEA72D5D6F0000213363 /* UIView+Extension.swift */; }; @@ -88,6 +89,7 @@ 9E4F285A27DF239162DD0D9F1179C563 /* TSAlertController.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TSAlertController.framework; sourceTree = BUILT_PRODUCTS_DIR; }; A43131703CCAA6A7B9991C036977C19D /* Pods-TSAlertController_Tests.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-TSAlertController_Tests.modulemap"; sourceTree = ""; }; BF03836614A23B9646B68177E4CAEC11 /* Pods-TSAlertController_Example.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-TSAlertController_Example.modulemap"; sourceTree = ""; }; + C700A09F2D8C716F00D5EFD8 /* UIBezierPath+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIBezierPath+Extension.swift"; sourceTree = ""; }; C79BCE8A2D5D6F0000213363 /* FadeInAndScaleDownAnimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FadeInAndScaleDownAnimator.swift; sourceTree = ""; }; C79BCE8B2D5D6F0000213363 /* SlideUpAnimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SlideUpAnimator.swift; sourceTree = ""; }; C79BCE8D2D5D6F0000213363 /* TSAlertPresentationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSAlertPresentationController.swift; sourceTree = ""; }; @@ -304,6 +306,7 @@ C79BCEA62D5D6F0000213363 /* UITextfield+Extension.swift */, C79BCEA72D5D6F0000213363 /* UIView+Extension.swift */, C79BCEC42D5D846E00213363 /* UIColor+Extension.swift */, + C700A09F2D8C716F00D5EFD8 /* UIBezierPath+Extension.swift */, ); path = Extensions; sourceTree = ""; @@ -544,6 +547,7 @@ buildActionMask = 2147483647; files = ( FCDD72576E464B783CB0432E06954BBA /* TSAlertController-dummy.m in Sources */, + C700A0A02D8C716F00D5EFD8 /* UIBezierPath+Extension.swift in Sources */, C79BCEAB2D5D6F0000213363 /* SlideUpAnimator.swift in Sources */, C79BCEAC2D5D6F0000213363 /* TSAlertController+Configuration.swift in Sources */, C79BCEAD2D5D6F0000213363 /* UIView+Extension.swift in Sources */, diff --git a/Sources/TSAlert/TSAlertController+ViewConfiguration.swift b/Sources/TSAlert/TSAlertController+ViewConfiguration.swift index 983f83e..1ca28b4 100644 --- a/Sources/TSAlert/TSAlertController+ViewConfiguration.swift +++ b/Sources/TSAlert/TSAlertController+ViewConfiguration.swift @@ -78,7 +78,7 @@ public extension TSAlertController { public var shadow: Shadow? /// The corner radius of the alert view. - public var cornerRadius: CGFloat + public var cornerRadius: CornerRadius /// The background color of the dimmed overlay behind the alert. public var dimmedBackgroundViewColor: Background? @@ -95,6 +95,46 @@ public extension TSAlertController { /// The axis layout of the button group (horizontal or vertical). public var buttonGroupAxis: ButtonGroupAxis + /// + public struct CornerRadius: ExpressibleByFloatLiteral { + + /// + public var topLeft: CGFloat + + /// + public var topRight: CGFloat + + /// + public var bottomLeft: CGFloat + + /// + public var bottomRight: CGFloat + + /// + public init(allCorners: CGFloat = 20) { + self.init(topLeft: allCorners, + topRight: allCorners, + bottomLeft: allCorners, + bottomRight: allCorners) + } + + /// + public init(topLeft: CGFloat = 20, + topRight: CGFloat = 20, + bottomLeft: CGFloat = 20, + bottomRight: CGFloat = 20) { + self.topLeft = topLeft + self.topRight = topRight + self.bottomLeft = bottomLeft + self.bottomRight = bottomRight + } + + /// + public init(floatLiteral value: FloatLiteralType) { + self.init(allCorners: value) + } + } + /// Defines the shadow properties of the alert. public struct Shadow { @@ -172,7 +212,7 @@ public extension TSAlertController { backgroundBorderColor: CGColor? = nil, backgroundBorderWidth: CGFloat = 0, shadow: Shadow? = nil, - cornerRadius: CGFloat = 20, + cornerRadius: CornerRadius = .init(allCorners: 20), dimmedBackgroundViewColor: Background? = .color(.black.withAlphaComponent(0.75)), margin: LayoutMargin = .init(), spacing: LayoutSpacing = .init(), diff --git a/Sources/TSAlert/TSAlertController.swift b/Sources/TSAlert/TSAlertController.swift index a74c405..bff41d3 100644 --- a/Sources/TSAlert/TSAlertController.swift +++ b/Sources/TSAlert/TSAlertController.swift @@ -233,6 +233,12 @@ public final class TSAlertController: UIViewController { activateFirstResponderIfNeeded() } + public override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + + view.applyRoundCorners(viewConfiguration.cornerRadius) + } + public override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) @@ -303,6 +309,7 @@ public final class TSAlertController: UIViewController { /// based on `TSAlertController.Style`. /// This ensures that the alert is presented correctly and prevents unintended behavior. private func adjustViewConfigurationForcibly() { + adjustCornerRadiusForcibly() adjustButtonGroupAxisForcibly() adjustActionOrderForcibly() } @@ -333,6 +340,17 @@ public final class TSAlertController: UIViewController { } } + /// + private func adjustCornerRadiusForcibly() { + switch preferredStyle { + case .actionSheet: + viewConfiguration.cornerRadius.bottomLeft = 0 + viewConfiguration.cornerRadius.bottomRight = 0 + default: + break + } + } + private func initializeAlertView() { setupAlertView() setupConstraints() @@ -384,7 +402,7 @@ public final class TSAlertController: UIViewController { view.layer.borderColor = viewConfiguration.backgroundBorderColor view.layer.borderWidth = viewConfiguration.backgroundBorderWidth - view.layer.cornerRadius = viewConfiguration.cornerRadius + view.applyRoundCorners(viewConfiguration.cornerRadius) guard let shadow = viewConfiguration.shadow else { return } view.layer.shadowColor = shadow.color diff --git a/Sources/TSAlert/TSAlertView/DefaultAlertView/DefaultAlertView.swift b/Sources/TSAlert/TSAlertView/DefaultAlertView/DefaultAlertView.swift index ec6f941..fa10d1b 100644 --- a/Sources/TSAlert/TSAlertView/DefaultAlertView/DefaultAlertView.swift +++ b/Sources/TSAlert/TSAlertView/DefaultAlertView/DefaultAlertView.swift @@ -87,12 +87,11 @@ class DefaultAlertView: UIView, TSAlertView { let actionHeight: CGFloat = viewConfiguration.buttonHeight let spacing: CGFloat = viewConfiguration.spacing.buttonSpacing - let height: CGFloat = if !isEmpty { - isHorizontal + var height: CGFloat = 0 + if !isEmpty { + height = isHorizontal ? actionHeight : (actionHeight * actionsCount) + ((actionsCount - 1) * spacing) - } else { - 0 } buttonGroupView.setHeight(equalTo: height) } diff --git a/Sources/Utils/Extensions/UIBezierPath+Extension.swift b/Sources/Utils/Extensions/UIBezierPath+Extension.swift new file mode 100644 index 0000000..0a1e98f --- /dev/null +++ b/Sources/Utils/Extensions/UIBezierPath+Extension.swift @@ -0,0 +1,59 @@ +// +// UIBezierPath+Extension.swift +// TSAlertController +// +// Created by 김건우 on 3/21/25. +// + +import UIKit + +extension UIBezierPath { + + convenience init(roundedRect rect: CGRect, + topLeftRadius: CGFloat = .zero, + topRightRadius: CGFloat = .zero, + bottomLeftRadius: CGFloat = .zero, + bottomRightRadius: CGFloat = .zero) { + self.init() + + let path = CGMutablePath() + + let topLeftPoint = CGPoint(x: rect.minX, y: rect.minY) + let topRightPoint = CGPoint(x: rect.maxX, y: rect.minY) + let bottomLeftPoint = CGPoint(x: rect.minX, y: rect.maxY) + let bottomRightPoint = CGPoint(x: rect.maxX, y: rect.maxY) + + path.move(to: bottomLeftPoint) + + path.addArc(tangent1End: topLeftPoint, + tangent2End: topRightPoint, + radius: topLeftRadius) + + path.addArc(tangent1End: topRightPoint, + tangent2End: bottomRightPoint, + radius: topRightRadius) + + path.addArc(tangent1End: bottomRightPoint, + tangent2End: bottomLeftPoint, + radius: bottomRightRadius) + + path.addArc(tangent1End: bottomLeftPoint, + tangent2End: topLeftPoint, + radius: bottomLeftRadius) + + path.closeSubpath() + cgPath = path + } +} + + +extension CGMutablePath { + + func addLine(toX x: CGFloat, y: CGFloat) { + addLine(to: CGPoint(x: x, y: y)) + } + + func move(toX x: CGFloat, y: CGFloat) { + move(to: CGPoint(x: x, y: y)) + } +} diff --git a/Sources/Utils/Extensions/UIView+Extension.swift b/Sources/Utils/Extensions/UIView+Extension.swift index 6394b2a..4b96b73 100644 --- a/Sources/Utils/Extensions/UIView+Extension.swift +++ b/Sources/Utils/Extensions/UIView+Extension.swift @@ -219,7 +219,7 @@ extension UIView { if let configuration = configuration { blurEffectView.layer.borderColor = configuration.backgroundBorderColor blurEffectView.layer.borderWidth = configuration.backgroundBorderWidth - blurEffectView.layer.cornerRadius = configuration.cornerRadius +// blurEffectView.layer.cornerRadius = configuration.cornerRadius blurEffectView.layer.masksToBounds = true } insertSubview(blurEffectView, at: 0) @@ -242,14 +242,14 @@ extension UIView { locations) if let viewConfiguration = viewConfiguration { - gradientView.gradientLayer?.cornerRadius = viewConfiguration.cornerRadius +// gradientView.gradientLayer?.cornerRadius = viewConfiguration.cornerRadius gradientView.gradientLayer?.masksToBounds = true } insertSubview(gradientView, at: 0) gradientView.fill(to: self) } - class GradientView: UIView { + final class GradientView: UIView { let gradientLayer: CAGradientLayer? @@ -310,3 +310,45 @@ extension UIView { self.layer.anchorPoint = anchorPoint } } + +extension UIView { + + // MARK: - Apply Round Corners + + /// + func applyRoundCorners(_ cornerRadius: CGFloat) { + + applyRoundCorners(topLeftRadius: cornerRadius, + topRightRadius: cornerRadius, + bottomLeftRadius: cornerRadius, + bottomRightRadius: cornerRadius) + } + + /// + func applyRoundCorners(_ cornerRadius: TSAlertController.ViewConfiguration.CornerRadius) { + + applyRoundCorners(topLeftRadius: cornerRadius.topLeft, + topRightRadius: cornerRadius.topRight, + bottomLeftRadius: cornerRadius.bottomLeft, + bottomRightRadius: cornerRadius.bottomRight) + } + + /// + func applyRoundCorners(topLeftRadius: CGFloat, + topRightRadius: CGFloat, + bottomLeftRadius: CGFloat, + bottomRightRadius: CGFloat) { + + let roundedRect = self.bounds + + let path = UIBezierPath(roundedRect: roundedRect, + topLeftRadius: topLeftRadius, + topRightRadius: topRightRadius, + bottomLeftRadius: bottomLeftRadius, + bottomRightRadius: bottomRightRadius) + + let shapeLayer = CAShapeLayer() + shapeLayer.path = path.cgPath + layer.mask = shapeLayer + } +}