Post

Replies

Boosts

Views

Activity

View with 2 labels of dynamic height
Hello, I try to do the same as the UIListContentConfiguration. Because I want to have a UITableViewCell with an image at beginning, then 2 Labels and an image at the end (accessoryView/Type should also be usable). I also want to have the dynamic behavior of the two labels, that if one or both of them exceeds a limit, that they are put under each under. But I cannot use the UIListContentConfiguration.valueCell, because of the extra image at the end. So I have tried to make a TextWrapper as an UIView, which contains only the two UILabels and the TextWrapper should take care of the dynamic height of the UILabels and put them side to side or under each other. But here in this post Im only concentrating on the issue with the labels under each other, because I have managed it to get it working, that I have two sets of Constraints and switch the activeStatus of the constraints, depending on the size of the two labels. But currently only the thing with the labels under each under makes problems. Following approaches I have tried for the TextWrapper: Using only constraints in this Wrapper (results in ambiguous constraints) Used combinations of constraints + intrinsicContentSize (failed because it seems that invalidateIntrinsicContentSize doesn't work for me) Approach with constraints only Following code snippet results in ambiguous vertical position and height. Because I have 3 constraints (those with the defaultHigh priorities). class TextWrapper: UIView { let textLabel = UILabel() let detailLabel = UILabel() init() { super.init(frame: .zero) self.addSubview(self.textLabel) self.addSubview(self.detailLabel) self.textLabel.numberOfLines = 0 self.detailLabel.numberOfLines = 0 self.directionalLayoutMargins = .init(top: 8, leading: 16, bottom: 8, trailing: 8) self.translatesAutoresizingMaskIntoConstraints = false self.textLabel.translatesAutoresizingMaskIntoConstraints = false self.detailLabel.translatesAutoresizingMaskIntoConstraints = false // Content Size self.textLabel.setContentCompressionResistancePriority(.required, for: .vertical) self.detailLabel.setContentCompressionResistancePriority(.required, for: .vertical) // Constraints self.textLabel.leadingAnchor.constraint(equalTo: self.layoutMarginsGuide.leadingAnchor).isActive = true self.textLabel.topAnchor.constraint(equalTo: self.layoutMarginsGuide.topAnchor).constraint(with: .defaultHigh) self.textLabel.widthAnchor.constraint(lessThanOrEqualTo: self.layoutMarginsGuide.widthAnchor).isActive = true self.detailLabel.leadingAnchor.constraint(equalTo: self.layoutMarginsGuide.leadingAnchor).isActive = true self.detailLabel.topAnchor.constraint(equalTo: self.textLabel.bottomAnchor, constant: 2).constraint(with: .defaultHigh) self.detailLabel.bottomAnchor.constraint(equalTo: self.layoutMarginsGuide.bottomAnchor).constraint(with: .defaultHigh) self.detailLabel.widthAnchor.constraint(lessThanOrEqualTo: self.layoutMarginsGuide.widthAnchor).isActive = true } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } Approach with intrinsicContentSize Pretty similar to the above, with only difference that I invalidate the intrinsicContentSize in the layoutSubviews, because at the first call of the intrinsicContentSize the width of the View is zero. I also tried different things like setNeedsLayout with layoutIfNeeded but nothing really works. After I invalidate the intrinsicContentSize in the layoutSubviews the intrinsicContentSize is called with the correct width of the View and calculates the correct height, but the TableView doesn't update the height accordingly. class TextWrapper: UIView { let textLabel = UILabel() let detailLabel = UILabel() init() { super.init(frame: .zero) self.addSubview(self.textLabel) self.addSubview(self.detailLabel) self.textLabel.numberOfLines = 0 self.detailLabel.numberOfLines = 0 self.directionalLayoutMargins = .init(top: 8, leading: 16, bottom: 8, trailing: 8) self.translatesAutoresizingMaskIntoConstraints = false self.textLabel.translatesAutoresizingMaskIntoConstraints = false self.detailLabel.translatesAutoresizingMaskIntoConstraints = false // Content Size self.textLabel.setContentCompressionResistancePriority(.required, for: .vertical) self.detailLabel.setContentCompressionResistancePriority(.required, for: .vertical) // Constraints self.textLabel.leadingAnchor.constraint(equalTo: self.layoutMarginsGuide.leadingAnchor).isActive = true self.textLabel.topAnchor.constraint(equalTo: self.layoutMarginsGuide.topAnchor).constraint(with: .defaultHigh) self.textLabel.widthAnchor.constraint(lessThanOrEqualTo: self.layoutMarginsGuide.widthAnchor).isActive = true self.detailLabel.leadingAnchor.constraint(equalTo: self.layoutMarginsGuide.leadingAnchor).isActive = true self.detailLabel.topAnchor.constraint(equalTo: self.textLabel.bottomAnchor, constant: 2).constraint(with: .defaultHigh) self.detailLabel.widthAnchor.constraint(lessThanOrEqualTo: self.layoutMarginsGuide.widthAnchor).isActive = true } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override var intrinsicContentSize: CGSize { let maxLabelWidth = self.bounds.width guard maxLabelWidth > 0 else { // The first time it has a width of 0, so we are giving a default height in this case, to dont produce a error in TableView return CGSize(width: UIView.noIntrinsicMetric, height: 44) } let textLabelSize = self.textLabel.sizeThatFits(CGSize(width: maxLabelWidth, height: .greatestFiniteMagnitude)) let detailLabelSize = self.detailLabel.sizeThatFits(CGSize(width: maxLabelWidth, height: .greatestFiniteMagnitude)) let totalHeight = textLabelSize.height + detailLabelSize.height + 16 return CGSize(width: UIView.noIntrinsicMetric, height: totalHeight) } override func layoutSubviews() { super.layoutSubviews() self.invalidateIntrinsicContentSize() } I also tried to use the intrinsicContentSize with only layoutSubviews, but this haven't worked also. Does anybody have ran into such issues? And know how to fix that?
2
0
303
Jun ’24
CloudKit NSPersistentCloudKitContainer Bug
Hi, does someone knows about the bug, that the NSPersistentCloudKitContainer doesn't return CKShares at the first time? Is this an intended behavior? My current fix: let container: NSPersistentCloudKitContainer = .... // Fix begin let managedObject = .... // Object which is in a share let record = container.record(for: managedObject.objectID) // Fix end let shares = (try? container.fetchShares(in: nil)) ?? [] If I execute exactly the same code without the fix that I fetch the record first, then it returns 0 shares. With the fix it is currently 9 shares (the actual count of the shares). Another fix: let container: NSPersistentCloudKitContainer = .... // Fix begin let _ = try? container.fetchShares(in: nil) Thread.sleep(forTimeInterval: 0.1) // Fix end let shares = (try? container.fetchShares(in: nil)) ?? [] For that fix it also returns at the second time the expected count of shares. But without the delay it returns also at the second time zero shares. Anyone had the same problem and if so, how do you solve it correctly? Thanks!
0
0
389
May ’24
CloudKit User Discoverability
Hello, Prior to iOS 17, it was possible to request user discoverability by calling ‚requestApplicationPermission‘. But now it is deprecated. How is the intended way to do this now? How this is for example handled in the Music App? I think this should be a similar approach. Currently the only option for me seems to save the Email-Address (and maybe phone number) in the CloudKit public database, that other user can discover by this. But that the problem exists, that we need to fetch for changes at phone numbers and mail addresses that are linked to the iCloud account (because user might change anything). Is there a more elegant solution, than just checking the current email addresses and phone numbers at each start of the application (if that is even possible)? For example the Music App has that Switch "Find my by Apple-ID" but I don't know how this is handled in the background. Thanks!
1
0
433
May ’24
Custom UIContentConfiguration for TableView Separator Insets
Hi, I have created a custom UIContentConfiguration + UIContentView for a TableView and using the custom Configuration and the default Configuration from UIListContentConfiguration in a same TableView (Style insetGrouped). But the separators from the custom configuration are different than that from the default configuration. And I didn't see an option to modify this through the custom configuration. Do anybody know how this is done with UIListContentConfiguration? For me currently the only option seems to be using "cell.separatorInset", but for the UIListContentConfiguration I don't need this. In the image below you see the problem. First, third and fifth cell is default configuration (UIListContentConfiguration.valueCell()). Second and fourth is the custom configuration. And under the custom configuration cells the separator is beginning at zero. This can be fixed with: cell.separatorInset.left = 20. This one is an example without image, but if I add an image for the default configuration, the text is much more behind and the fix with cell.separatorInset.left = 20 is not working in this case. For default configuration: let cell = tableView.dequeueReusableCell(withIdentifier: "default", for: indexPath) var config = UIListContentConfiguration.valueCell() config.text = "Test" config.secondaryText = "Test" cell.contentConfiguration = config return cell For custom configuration: Configuration + ContentView: struct SwitchConfiguration: UIContentConfiguration { var text: String? var value: Bool var delegate: SwitchDelegate? func makeContentView() -> UIView & UIContentView { return SwitchContentView(configuration: self) } func updated(for state: UIConfigurationState) -> SwitchConfiguration { return self } } class SwitchContentView: UIView, UIContentView { var configuration: UIContentConfiguration { didSet { self.configure() } } private let textLabel: UILabel = UILabel() private let `switch`: UISwitch = UISwitch() init(configuration: UIContentConfiguration) { self.configuration = configuration super.init(frame: .zero) self.switch.addTarget(self, action: #selector(self.switchValueDidChange(_:)), for: .valueChanged) self.addSubview(self.textLabel) self.addSubview(self.switch) self.directionalLayoutMargins = .init(top: 8, leading: 20, bottom: 8, trailing: 8) self.textLabel.translatesAutoresizingMaskIntoConstraints = false self.textLabel.leadingAnchor.constraint(equalTo: self.layoutMarginsGuide.leadingAnchor).isActive = true self.textLabel.centerYAnchor.constraint(equalTo: self.layoutMarginsGuide.centerYAnchor).isActive = true self.switch.translatesAutoresizingMaskIntoConstraints = false self.switch.trailingAnchor.constraint(equalTo: self.layoutMarginsGuide.trailingAnchor, constant: -16).isActive = true self.switch.centerYAnchor.constraint(equalTo: self.layoutMarginsGuide.centerYAnchor).isActive = true self.switch.topAnchor.constraint(equalTo: self.layoutMarginsGuide.topAnchor).isActive = true self.switch.bottomAnchor.constraint(equalTo: self.layoutMarginsGuide.bottomAnchor).isActive = true self.configure() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func configure() { guard let config = self.configuration as? SwitchConfiguration else { return } self.textLabel.text = config.text self.switch.isOn = config.value } @IBAction func switchValueDidChange(_ switch: UISwitch) { guard let config = self.configuration as? SwitchConfiguration else { return } config.delegate?.switchValueDidChange(`switch`.isOn) } } Cell: let cell = tableView.dequeueReusableCell(withIdentifier: "custom", for: indexPath) var config = SwitchConfiguration() config.text = "Test" config.value = true cell.contentConfiguration = config //cell.separatorInset.left = 20 return cell
0
1
576
Jan ’24
NWConnection on Watch - WiFi not working
Hey,im trying to get the NWConnection on watchOS 6 working. The NWConnection is working completly fine in combination with the iPhone, but if I disconnect the iPhone (Airplane Mode, WiFi and Bluetooth Off) the connection is not working, but the apple watch is connected with the wifi.Normally the Apple Watch (mine a Series 4) can connect to a server without an iPhone, too?Here the minimal example code (Created Watch-App iOS-Project with XCode 11.5 - InterfaceBuilder). The only thing I changed is I added an Label to the Watch-Interface and the following InterfaceController.swift in WatchKit Extension:(Just for Info. The Server will just return "OK MPD 0.19.0\n" at connect)import WatchKit import Foundation import Network class InterfaceController: WKInterfaceController { @IBOutlet weak var label: WKInterfaceLabel! private var connection : NWConnection? private var dispatchQueue = DispatchQueue(label: "connection") override func awake(withContext context: Any?) { super.awake(withContext: context) } override func willActivate() { // This method is called when watch view controller is about to be visible to user super.willActivate() self.label.setText("Version") let endpoint = NWEndpoint.hostPort(host: .name("192.168.178.10", nil), port: 6600) let parameters = NWParameters.tcp self.connection = NWConnection(to: endpoint, using: parameters) self.connection?.stateUpdateHandler = .some({ (state) in print("Received State=\(state)") switch state { case .ready: self.label.setText("READY") self.connection?.receive(minimumIncompleteLength: 1, maximumLength: 1024, completion: { (data, contentContext, isComplete, error) in if let data = data, data.count > 0, let string = String(data: data, encoding: .utf8) { let arr = string.components(separatedBy: "\n") let VERSION_PREFIX = "OK MPD" for item in arr { if item.hasPrefix(VERSION_PREFIX) { // Received Version let version = item[item.index(item.startIndex, offsetBy: VERSION_PREFIX.count)...] var text = String(version) if let path = self.connection?.currentPath { text += " - Uses Others=\(path.usesInterfaceType(.other)) Uses Wifi=\(path.usesInterfaceType(.wifi))" } self.label.setText(text) break } } } }) break case .waiting(let error): self.label.setText("Waiting Error=\(error)") break case .failed(let error): self.label.setText("Failed Error=\(error)") default: break } }) self.connection?.start(queue: self.dispatchQueue) } override func didDeactivate() { // This method is called when watch view controller is no longer visible self.connection?.cancel() super.didDeactivate() } }It is because the Watch could not connect to a server without the iPhone or am I missing something?Thanks and RegardsAdrian
1
0
952
May ’20