Post

Replies

Boosts

Views

Activity

Debug mysterious window resize?
I am unfortunately faced with a large legacy code base in which Storyboards are heavily used. Now, for some reason, the entire app window is resized if a certain View Controller becomes visible. The issue: Apparently, there aren't any conflicting layout constraints (no LAYOUT_CONSTRAINTS_NOT_SATISFIABLE errors are raised on display of the view controller). There are also no calls to setFrame on the corresponding window. So, how do I debug this? Capturing the view hierarchy didn't provide any helpful insights, and ideally I could just force the window to not resize (due to possible constraint errors). Is there any way to achieve something like this? If not, how can I go about debugging this? Any help on this would be greatly appreciated.
0
0
543
May ’24
NWConnection UDP Broadcast not sent out correctly
Problem I am trying to send out a broadcast using NWConnection and then listen for responses using NWListener on port 50913. Although the broadcast is sent out correctly (= no error is thrown upon sending), I only get responses to my broadcast from what I suppose are the network interfaces of my own MacBook. In other words, it seems like the broadcast is never really submitted to the network. Context I don't have in-depth knowledge about the behavior of UDP which is why I am confused about this behavior. I've been reading online about this and couldn't find anything really related to the behavior I am experiencing. I've also looked at this developer forums entry and implemented the broadcast accordingly. The response from @meaton does not suggest that broadcasts are not supported by NWConnection (which is what I thought to be the culprit initially), and I am not getting the error they are talking about in their post, but a behavior that is entirely different. Does anyone know what is wrong with my implementation? Code final public class BroadcastDiscoveryEngine { private let logger: Logger = Logger.init(for: BroadcastDiscoveryEngine.self) private let broadcastConnection: NWConnection private let broadcastResponseListener: NWListener private let responseParser: BroadcastResponseParser = BroadcastResponseParser() private var discoveryContinuation: AsyncStream<Discovery>.Continuation? = nil init() throws { let parameters = NWParameters.udp parameters.allowLocalEndpointReuse = true parameters.allowFastOpen = true parameters.includePeerToPeer = true broadcastConnection = NWConnection(host: .ipv4(.broadcast), port: .init(integerLiteral: 50913), using: parameters) broadcastResponseListener = try NWListener(using: parameters, on: 50913) } func startBroadcast(continuation: AsyncStream<Discovery>.Continuation) { discoveryContinuation = continuation broadcastConnection.stateUpdateHandler = handleBroadcastConnectionStateUpdate(state:) broadcastConnection.start(queue: .global(qos: .default)) startBroadcastListener() } func stopBroadcast() { broadcastConnection.cancel() broadcastResponseListener.cancel() } private func sendBroadcastMessage() { broadcastConnection.send(content: "my_broadcast_message".data(using: .utf8), completion: .contentProcessed({ error in if let error = error { self.logger.error("Sending broadcast message failed with error: \(error.debugDescription, privacy: .public)") self.broadcastConnection.cancel() self.broadcastResponseListener.cancel() } self.logger.info("Broadcast message sent.") })) } private func handleBroadcastConnectionStateUpdate(state: NWConnection.State) { switch state { // shortened other cases since only logging occurs case .ready: logger.info("Broadcast connection established, ready to send and receive data.") sendBroadcastMessage() } } } extension BroadcastDiscoveryEngine { private func startBroadcastListener() { broadcastResponseListener.stateUpdateHandler = handleBroadcastResponseListenerStateUpdate(state:) broadcastResponseListener.newConnectionHandler = handleIncomingConnection(connection:) broadcastResponseListener.start(queue: .global(qos: .default)) } private func handleBroadcastResponseListenerStateUpdate(state: NWListener.State) { switch state { // shortened cases since only logging occurs } } private func handleIncomingConnection(connection: NWConnection) { connection.stateUpdateHandler = { state in self.handleIncomingConnectionStateUpdate(connection: connection, state: state) } connection.start(queue: .global(qos: .default)) } private func handleIncomingConnectionStateUpdate(connection: NWConnection, state: NWConnection.State) { switch state { // shortened other cases since only logging occurs case .ready: logger.info("Incoming connection (\(connection.debugDescription, privacy: .public) established, ready to send and receive data.") connection.receiveMessage { content, contentContext, isComplete, error in self.receiveBroadcastResponse(connection: connection, content: content, contentContext: contentContext, isComplete: isComplete, error: error) } } } private func receiveBroadcastResponse(connection: NWConnection, content: Data?, contentContext: NWConnection.ContentContext?, isComplete: Bool, error: NWError?) { // shortened: handles parsing accordingly and then cancels connection connection.cancel() } }
2
0
663
Dec ’23
UIView resizing animation snaps into updated bounds before animation is done
Problem: I am trying to create my own custom search field with a desired growing animation (if you click on it), and a shrinking animation when the user taps out. The animation behaves weirdly since it moves out of the right screen bounds when shrinking, even though the text field/search bar's right anchor is not modified. Like so: Notice how the right side of the search bar briefly moves outside of the visible screen bounds during the animation. Expected behavior: The search bar should smoothly grow/shrink without moving the right edge position of the text field, i.e. have the right anchor stay pinned. What you see in above gif is built using the following code (by subclassing a UITextField): public class MySearchBar: UITextField { private var preAnimationWidth: NSLayoutConstraint? private var postAnimationWidth: NSLayoutConstraint? public override init(frame: CGRect) { super.init(frame: frame) self.backgroundColor = Theme.GRAY800 self.borderStyle = .roundedRect self.layer.masksToBounds = true self.clipsToBounds = true self.autocorrectionType = .no self.font = FontFamily.ProximaNova.regular.font(size: 16) self.textColor = .white self.attributedPlaceholder = NSAttributedString(string: "Search", attributes: [.foregroundColor : Theme.GRAY400, .font: FontFamily.ProximaNova.regular.font(size: 16)]) // some further appearance configurations } public func setupGrowAnimation(initialWidth: NSLayoutConstraint, grownWidth: NSLayoutConstraint, height: CGFloat) { preAnimationWidth = initialWidth postAnimationWidth = grownWidth self.layer.borderWidth = 0 self.layer.cornerRadius = height / 2 } // growButton is called when the textfield becomes active, i.e. the user taps on it. public func growButton() { guard let preAnimationWidth = preAnimationWidth, let postAnimationWidth = postAnimationWidth else { return } UIView.animate(withDuration: 0.2) { preAnimationWidth.isActive = false postAnimationWidth.isActive = true self.layer.borderColor = Theme.GRAY600.cgColor self.layer.borderWidth = 2 self.layer.cornerRadius = 8 self.layoutIfNeeded() } } // shrinkButton is called whenever the textfield resigns its first responder state, i.e. the user clicks out of it. public func shrinkButton() { guard let preAnimationWidth = preAnimationWidth, let postAnimationWidth = postAnimationWidth else { return } UIView.animate(withDuration: 0.2) { postAnimationWidth.isActive = false preAnimationWidth.isActive = true self.layer.borderWidth = 0 self.layer.borderColor = .none self.layer.cornerRadius = self.frame.height / 2 self.layoutIfNeeded() } } } And this is how the search bar is initialized in my viewDidLoad: override func viewDidLoad() { let containerView = UIView() let searchBar = MySearchBar() searchBar.addTarget(self, action: #selector(searchBarChangedEntry(_:)), for: .editingChanged) searchBar.addTarget(self, action: #selector(searchBarEndedEditing(_:)), for: .editingDidEnd) searchBar.translatesAutoresizingMaskIntoConstraints = false let initialWidth = searchBar.widthAnchor.constraint(equalToConstant: 100) let expandedWidth = searchBar.widthAnchor.constraint(equalTo: containerView.widthAnchor, constant: -32) searchBar.setupGrowAnimation(initialWidth: initialWidth, grownWidth: expandedWidth, height: 44) containerView.addSubview(searchBar) stackView.insertArrangedSubview(containerView, at: 0) NSLayoutConstraint.activate([ containerView.heightAnchor.constraint(equalToConstant: 44), containerView.widthAnchor.constraint(equalTo: self.stackView.widthAnchor), searchBar.heightAnchor.constraint(equalTo: containerView.heightAnchor), initialWidth, searchBar.rightAnchor.constraint(equalTo: containerView.rightAnchor, constant: -16) ]) self.stackView.setCustomSpacing(12, after: containerView) } The search bar is part of a container view which, in turn, is the first (top) arranged subview of a stack view covering the entire screen's safeAreaLayout rectangle What I already tried: I have to perform the animation using constraints, and I've tried to animate it without using the width anchor (e.g. by animating the leftAnchor's constant). Nothing worked so far. Upon googling, I couldn't really find anything helpful that would help me find a solution to this problem, which is why I am trying my luck here. I do have to admit that I am not proficient with animations of iOS at all - so please bear with me if this is a simple mistake to fix. So, why does the search bar behave that way? And how can I fix this?
1
1
2.1k
Oct ’22
NSTextField not showing attributed string
I am trying to create a simple NSTextField where the entire text content should be a link. The text field is created/positioned in Interface Builder and later propagated with actual data by the view controller. However, the text field does not display my attributed string. In order to keep things neat and tidy, I subclassed NSTextField like so: public class MyLink: NSTextField {     private var linkRange: NSRange?     override init(frame frameRect: NSRect) {         super.init(frame: frameRect)     }     required init?(coder: NSCoder) {         super.init(coder: coder)     }     public func setLinkContent(text: String, to url: URL, range: NSRange?) {         allowsEditingTextAttributes = true         isSelectable = true         let attributedLink = NSMutableAttributedString(string: text)         self.linkRange = range ?? NSRange(location: 0, length: text.count)         attributedLink.addAttribute(.link, value: url, range: self.linkRange!)         attributedLink.addAttributes([             .foregroundColor: NSColor.red,             .underlineStyle: NSUnderlineStyle.single,             .cursor: NSCursor.pointingHand,             .font: NSFont.systemFont(ofSize: 16)         ], range: self.linkRange!)         self.attributedStringValue = attributedLink     } } To put things in perspective, the view controller calls setLinkContent to propagate the text field data. And after that - nothing (see image). You see that the text field is there because of the blue background color I set, but the actual text is not propagated. Configuration of the TextField in IB:
0
1
1.1k
Oct ’22