@@ -61,8 +61,7 @@ class UIMessageView: UIView {
61
61
label. font = . systemFont( ofSize: 18 )
62
62
label. textColor = textColor
63
63
label. numberOfLines = 0
64
- // label.setContentHuggingPriority(.defaultLow, for: .horizontal)
65
- // label.setContentCompressionResistancePriority(.required, for: .horizontal)
64
+
66
65
return label
67
66
} ( )
68
67
@@ -105,11 +104,7 @@ class UIMessageView: UIView {
105
104
}
106
105
107
106
private var isMultiline : Bool {
108
- if let file = fullMessage. file,
109
- let width = file. width,
110
- let height = file. height,
111
- height > width && width < 250
112
- {
107
+ if fullMessage. file != nil {
113
108
return true
114
109
}
115
110
@@ -181,23 +176,63 @@ class UIMessageView: UIView {
181
176
182
177
private func setupMessageContainer( ) {
183
178
if isMultiline {
184
- multiLineContainer. addArrangedSubview ( messageLabel)
179
+ setupMultilineMessage ( )
180
+ } else {
181
+ setupSingleLineMessage ( )
182
+ }
183
+ }
184
+
185
+ private func setupMultilineMessage( ) {
186
+ if message. hasFile, message. hasText {
187
+ let innerContainer = UIStackView ( )
188
+ innerContainer. axis = . vertical
189
+ innerContainer. translatesAutoresizingMaskIntoConstraints = false
190
+ innerContainer. layoutMargins = UIEdgeInsets (
191
+ top: 0 ,
192
+ left: StackPadding . leading,
193
+ bottom: 0 ,
194
+ right: StackPadding . trailing
195
+ )
196
+ innerContainer. spacing = 10
197
+ innerContainer. isLayoutMarginsRelativeArrangement = true
198
+ innerContainer. insetsLayoutMarginsFromSafeArea = false
199
+
200
+ innerContainer. addArrangedSubview ( messageLabel)
185
201
186
202
let metadataContainer = UIStackView ( )
187
203
metadataContainer. axis = . horizontal
188
- metadataContainer. addArrangedSubview ( UIView ( ) ) // Spacer
204
+ metadataContainer. translatesAutoresizingMaskIntoConstraints = false
205
+ metadataContainer. addArrangedSubview ( UIView ( ) )
189
206
metadataContainer. addArrangedSubview ( metadataView)
190
- multiLineContainer . addArrangedSubview ( metadataContainer)
207
+ innerContainer . addArrangedSubview ( metadataContainer)
191
208
192
- containerStack. addArrangedSubview ( multiLineContainer)
193
- } else {
194
- singleLineContainer. addArrangedSubview ( messageLabel)
195
- singleLineContainer. addArrangedSubview ( metadataView)
209
+ multiLineContainer. addArrangedSubview ( innerContainer)
196
210
197
- containerStack. addArrangedSubview ( singleLineContainer)
211
+ containerStack. addArrangedSubview ( innerContainer)
212
+ } else {
213
+ multiLineContainer. addArrangedSubview ( messageLabel)
214
+ if !message. hasFile || message. hasText {
215
+ setupMultilineMetadata ( )
216
+ containerStack. addArrangedSubview ( multiLineContainer)
217
+ return
218
+ }
198
219
}
199
220
}
200
221
222
+ private func setupMultilineMetadata( ) {
223
+ let metadataContainer = UIStackView ( )
224
+ metadataContainer. axis = . horizontal
225
+ metadataContainer. addArrangedSubview ( UIView ( ) ) // Spacer
226
+ metadataContainer. addArrangedSubview ( metadataView)
227
+ multiLineContainer. addArrangedSubview ( metadataContainer)
228
+ }
229
+
230
+ private func setupSingleLineMessage( ) {
231
+ singleLineContainer. addArrangedSubview ( messageLabel)
232
+ singleLineContainer. addArrangedSubview ( metadataView)
233
+ containerStack. addArrangedSubview ( singleLineContainer)
234
+ }
235
+
201
236
private func addGestureRecognizer( ) {
202
237
bubbleView. isUserInteractionEnabled = true
203
238
messageLabel. isUserInteractionEnabled = true
@@ -206,29 +241,98 @@ class UIMessageView: UIView {
206
241
bubbleView. addGestureRecognizer ( tapGesture)
207
242
}
208
243
244
+ enum StackPadding {
245
+ static let top : CGFloat = 9
246
+ static let leading : CGFloat = 12
247
+ static let bottom : CGFloat = 9
248
+ static let trailing : CGFloat = 12
249
+ }
250
+
209
251
private func setupConstraints( ) {
210
- NSLayoutConstraint . activate ( [
252
+ let padding = NSDirectionalEdgeInsets (
253
+ top: StackPadding . top,
254
+ leading: StackPadding . leading,
255
+ bottom: isMultiline ? 14 : StackPadding . bottom,
256
+ trailing: StackPadding . trailing
257
+ )
258
+
259
+ let baseConstraints : [ NSLayoutConstraint ] = [
211
260
bubbleView. topAnchor. constraint ( equalTo: topAnchor) ,
212
261
bubbleView. bottomAnchor. constraint ( equalTo: bottomAnchor) ,
213
262
bubbleView. widthAnchor. constraint ( lessThanOrEqualTo: widthAnchor, multiplier: 0.9 ) ,
263
+ ]
264
+
265
+ let withoutFileConstraints : [ NSLayoutConstraint ] = [
266
+ containerStack. topAnchor. constraint (
267
+ equalTo: bubbleView. topAnchor,
268
+ constant: padding. top
269
+ ) ,
270
+ containerStack. leadingAnchor. constraint (
271
+ equalTo: bubbleView. leadingAnchor,
272
+ constant: padding. leading
273
+ ) ,
274
+ containerStack. trailingAnchor. constraint (
275
+ equalTo: bubbleView. trailingAnchor,
276
+ constant: - padding. trailing
277
+ ) ,
278
+ containerStack. bottomAnchor. constraint (
279
+ equalTo: bubbleView. bottomAnchor,
280
+ constant: - padding. bottom
281
+ ) . withPriority ( . defaultHigh) ,
282
+ ]
214
283
284
+ let withFileConstraints : [ NSLayoutConstraint ] = [
215
285
containerStack. topAnchor. constraint (
216
286
equalTo: bubbleView. topAnchor,
217
- constant: labelVerticalPadding
287
+ constant: 0
218
288
) ,
219
289
containerStack. leadingAnchor. constraint (
220
290
equalTo: bubbleView. leadingAnchor,
221
- constant: labelHorizantalPadding
291
+ constant: 0
222
292
) ,
223
293
containerStack. trailingAnchor. constraint (
224
294
equalTo: bubbleView. trailingAnchor,
225
- constant: - labelHorizantalPadding
295
+ constant: 0
226
296
) ,
227
297
containerStack. bottomAnchor. constraint (
228
298
equalTo: bubbleView. bottomAnchor,
229
- constant: isMultiline ? - 14 : - labelVerticalPadding
299
+ constant: 0
230
300
) . withPriority ( . defaultHigh) ,
231
- ] )
301
+ ]
302
+
303
+ let withFileAndTextConstraints : [ NSLayoutConstraint ] = [
304
+ containerStack. topAnchor. constraint (
305
+ equalTo: bubbleView. topAnchor,
306
+ constant: 0
307
+ ) ,
308
+ containerStack. leadingAnchor. constraint (
309
+ equalTo: bubbleView. leadingAnchor,
310
+ constant: 0
311
+ ) ,
312
+ containerStack. trailingAnchor. constraint (
313
+ equalTo: bubbleView. trailingAnchor,
314
+ constant: 0
315
+ ) ,
316
+ containerStack. bottomAnchor. constraint (
317
+ equalTo: bubbleView. bottomAnchor,
318
+ constant: - padding. bottom
319
+ ) . withPriority ( . defaultHigh) ,
320
+ ]
321
+
322
+ let constraints : [ NSLayoutConstraint ] = switch ( message. hasFile, message. hasText) {
323
+ case ( true , false ) :
324
+ // File only
325
+ withFileConstraints
326
+ case ( true , true ) :
327
+ // File with text
328
+ withFileAndTextConstraints
329
+ default :
330
+ // Text only
331
+ withoutFileConstraints
332
+ }
333
+
334
+ NSLayoutConstraint . activate ( baseConstraints + constraints)
335
+
232
336
if outgoing {
233
337
bubbleView. trailingAnchor. constraint ( equalTo: trailingAnchor, constant: - 8 ) . isActive = true
234
338
} else {
@@ -575,3 +679,14 @@ extension String {
575
679
contains { $0. isEmoji }
576
680
}
577
681
}
682
+
683
+ extension Message {
684
+ var hasFile : Bool {
685
+ fileId != nil
686
+ }
687
+
688
+ var hasText : Bool {
689
+ guard let text else { return false }
690
+ return !text. isEmpty
691
+ }
692
+ }
0 commit comments