I have a SwiftUI view which shows image and text.
import SwiftUI
#if canImport(UIKit)
typealias AppColor = UIColor
#else
typealias AppColor = NSColor
#endif
struct ReportErrorUI: View {
enum Order {
case even, odd
}
let error: String
let order: Order
init(error: String, order: Order) {
self.error = error
self.order = order
}
var body: some SwiftUI.View {
let bgColor = (order == .even ? AppColor.systemGreen : .systemRed).withAlphaComponent(0.5)
return VStack(alignment: .leading, spacing: 0, content: {
HStack(alignment: .center, spacing: 8, content: {
Image("icon.error")
Text(error).font(.body)
}).padding(8)
Color(.magenta).frame(height: 2)
}).background(Color(bgColor))
}
}
Now I can preview it:
import SwiftUI
@available(OSX 11.0, *)
struct ReportErrorUI_Previews: PreviewProvider {
static let shortText = "Etiam habebis sem dicantur magna mollis euismod."
static let longText = "Tityre, tu patulae recubans sub tegmine fagi dolor. Idque Caesaris facere voluntate liceret: sese habere. Unam incolunt Belgae, aliam Aquitani, tertiam."
static var previews: some SwiftUI.View {
Group {
VStack(spacing: 0) {
ReportErrorUI(error: longText, order: .even)
ReportErrorUI(error: shortText, order: .odd)
}.previewLayout(.sizeThatFits)
.background(Color.white).padding().frame(width: 320)
}
}
}
Here is it looks like:
As you can see the Text view works as expected – long text is wrapped.
Now I am adding this ReportErrorUI alongside with old-school NSTextField multiline label, into existing AppKit application via NSHostingController or NSHostingView. Both UI elements added into NSStackView.
class MainViewController: ReusableViewController {
private lazy var stackView = NSStackView()
override func setupUI() {
view.addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.topAnchor.constraint(equalTo: view.topAnchor, constant: 8).isActive = true
stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -8).isActive = true
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 8).isActive = true
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -8).isActive = true
stackView.widthAnchor.constraint(greaterThanOrEqualToConstant: 120).isActive = true
stackView.heightAnchor.constraint(greaterThanOrEqualToConstant: 80).isActive = true
stackView.orientation = .vertical
stackView.spacing = 8
stackView.distribution = .fill
stackView.alignment = .leading
// Wrappable multiline label. See: https://stackoverflow.com/a/35920653/1418981
let label = NSTextField()
label.stringValue = "Excepteur sint obcaecat cupiditat non proident culpa. A communi observantia non est recedendum."
label.setContentHuggingPriority(.init(1), for: .horizontal)
label.setContentCompressionResistancePriority(.init(1), for: .horizontal)
label.isEditable = false
label.backgroundColor = NSColor.magenta.withAlphaComponent(0.4)
label.isBezeled = false
label.isSelectable = true
//> Below 4 lines seems not needed.
// label.usesSingleLineMode = false
// label.lineBreakMode = .byWordWrapping
// label.cell?.wraps = true
// label.cell?.isScrollable = false
//<
stackView.addArrangedSubview(label)
do {
let ui = ReportErrorUI(error: "Excepteur sint obcaecat cupiditat non proident culpa. A communi observantia non est recedendum.", order: .even)
let view = NSHostingView(rootView: ui)
view.setContentHuggingPriority(.init(1), for: .horizontal)
view.setContentCompressionResistancePriority(.init(1), for: .horizontal)
view.setContentCompressionResistancePriority(.required, for: .vertical)
stackView.addArrangedSubview(view)
}
do {
let ui = ReportErrorUI(error: "Excepteur sint obcaecat cupiditat non proident culpa. A communi observantia non est recedendum.", order: .odd)
let vc = NSHostingController(rootView: ui)
addChild(vc)
vc.view.setContentHuggingPriority(.init(1), for: .horizontal)
vc.view.setContentCompressionResistancePriority(.init(1), for: .horizontal)
vc.view.setContentCompressionResistancePriority(.required, for: .vertical)
stackView.addArrangedSubview(vc.view)
}
do {
let view = ReusableView()
view.backgroundColor = .yellow
view.heightAnchor.constraint(greaterThanOrEqualToConstant: 12).isActive = true
stackView.addArrangedSubview(view)
}
}
}
As result old-school NSTextField works as expected - multiline text wraps itself on Application window resize. But text inside SwiftUI Text view is not wrapped.
Tricks from UIKit world, like lineLimit(nil) or .fixedSize(horizontal: false, vertical: true), either not working or breaking Application window layout.
Here for instance how .fixedSize(horizontal: false, vertical: true) works - Application window layout is broken. Application window cannot be vertically resized.
Any combination of setContentCompressionResistancePriority or setContentHuggingPriority for horizontal, vertical axes set on NSHostingView or NSHostingController not helping.
How to make multiline wrapping SwiftUI Text working correctly on macOS?


