@@ -57,6 +57,8 @@ public class LMAlert: UIView {
5757 // MARK: - Properties
5858 private var cancelAction : ( ( ) -> Void ) ?
5959 private var confirmAction : ( ( ) -> Void ) ?
60+ private var titleTopConstraint : Constraint ?
61+ private var buttonTopConstraint : Constraint ?
6062
6163 // MARK: - Initialization
6264 public init ( title: String , cancelTitle: String = " 아니요 " , confirmTitle: String = " 네 " ) {
@@ -102,7 +104,7 @@ public class LMAlert: UIView {
102104 }
103105
104106 titleLabel. snp. makeConstraints {
105- $0. top. equalToSuperview ( ) . inset ( 24 )
107+ self . titleTopConstraint = $0. top. equalToSuperview ( ) . inset ( 24 ) . constraint
106108 $0. horizontalEdges. equalToSuperview ( ) . inset ( 20 )
107109 $0. bottom. lessThanOrEqualTo ( buttonStackView. snp. top) . offset ( - 16 )
108110 }
@@ -111,7 +113,7 @@ public class LMAlert: UIView {
111113 $0. bottom. equalToSuperview ( ) . inset ( 20 )
112114 $0. horizontalEdges. equalToSuperview ( ) . inset ( 20 )
113115 $0. height. equalTo ( 44 )
114- $0. top. greaterThanOrEqualTo ( titleLabel. snp. bottom) . offset ( 16 )
116+ self . buttonTopConstraint = $0. top. greaterThanOrEqualTo ( titleLabel. snp. bottom) . offset ( 16 ) . constraint
115117 }
116118 }
117119
@@ -137,7 +139,10 @@ public class LMAlert: UIView {
137139 // cancelTitle이 비어있으면 취소 버튼 숨기기
138140 if cancelTitle. isEmpty {
139141 cancelButton. isHidden = true
142+ } else {
143+ cancelButton. isHidden = false
140144 }
145+ updateSpacing ( for: attributedString)
141146 }
142147
143148 // MARK: - Actions
@@ -195,4 +200,40 @@ public class LMAlert: UIView {
195200 completion ( )
196201 }
197202 }
203+
204+ // MARK: - Layout Helpers
205+ private func updateSpacing( for attributedString: NSAttributedString ) {
206+ let contentWidth : CGFloat = 280 - 40 // container width - horizontal inset
207+ let lineCount = calculateLineCount ( for: attributedString, maxWidth: contentWidth)
208+ let spacing = spacingConfiguration ( for: lineCount)
209+ titleTopConstraint? . update ( offset: spacing. topInset)
210+ buttonTopConstraint? . update ( offset: spacing. betweenSpacing)
211+ layoutIfNeeded ( )
212+ }
213+
214+ private func spacingConfiguration( for lineCount: Int ) -> ( topInset: CGFloat , betweenSpacing: CGFloat ) {
215+ switch lineCount {
216+ case ..< 2 :
217+ return ( 32 , 24 )
218+ case 2 :
219+ return ( 28 , 20 )
220+ default :
221+ return ( 24 , 16 )
222+ }
223+ }
224+
225+ private func calculateLineCount( for attributedString: NSAttributedString , maxWidth: CGFloat ) -> Int {
226+ guard attributedString. length > 0 else { return 1 }
227+ let boundingRect = attributedString. boundingRect (
228+ with: CGSize ( width: maxWidth, height: . greatestFiniteMagnitude) ,
229+ options: [ . usesLineFragmentOrigin, . usesFontLeading] ,
230+ context: nil
231+ )
232+ let font = ( attributedString. attribute ( . font, at: 0 , effectiveRange: nil ) as? UIFont ) ?? titleLabel. font ?? . systemFont( ofSize: 16 )
233+ let paragraphStyle = attributedString. attribute ( . paragraphStyle, at: 0 , effectiveRange: nil ) as? NSParagraphStyle
234+ let lineHeight = font. lineHeight + ( paragraphStyle? . lineSpacing ?? 0 )
235+ if lineHeight == 0 { return 1 }
236+ let rawCount = Int ( ceil ( boundingRect. height / lineHeight) )
237+ return max ( 1 , rawCount)
238+ }
198239}
0 commit comments