Creating iOS context menu with highlight and dim

The iOS messaging application I work on features a context menu in the chat. You long-press any message in the chat and the context menu appears. This menu was originally implemented using the standard UIMenuController.

The UIMenuController is an old-style iOS API that is hard to use and does not work very well. In some situations tapping its items just did not call the assigned selectors and the menu did not work.

As part of the ongoing redesign of the application I decided to implement a new custom context menu that would look as the designer imagined and more importantly work reliably. I did not want to use any 3rd party library to keep it as simple and possible.

Using just UIKit I came up with a context menu with a dim effect and a highlight on the selected item

Here is how I approached building it.

[Read More]
iOS  Swift  Xcode 

Detecting click on a specific NSTableViewCell

When you use NSTableView in an macOS application, there is no direct way to know a specific NSTableViewCell was clicked by the user. In my Localization Editor project I wanted the user to be able to focus a NSTextField when clicking anywhere in the NSTableViewCell it is contained in, so I had to implement it myself.

I created a new delegate extending the NSTableViewDelegate with one additional method informing about a NSTableViewCell getting clicked

protocol NSTableViewClickableDelegate: NSTableViewDelegate {
    func tableView(_ tableView: NSTableView, didClickRow row: Int, didClickColumn: Int)

Then I added an extension to the NSTableView to compute the index of the clicked NSTableViewCell

extension NSTableView {
    open override func mouseDown(with event: NSEvent) {
        let localLocation = self.convert(event.locationInWindow, to: nil)
        let clickedRow = self.row(at: localLocation)
        let clickedColumn = self.column(at: localLocation)

        super.mouseDown(with: event)

        guard clickedRow >= 0, clickedColumn >= 0, let delegate = self.delegate as? NSTableViewClickableDelegate else {

        delegate.tableView(self, didClickRow: clickedRow, didClickColumn: clickedColumn)

To be able to use this extension you just need to implement NSTableViewClickableDelegate instead of NSTableViewDelegate and use the additional method it provides.

macos  swift 

Making copy & paste work with NSTextField

When I started working on my open-source Localization Editor, which is a macOS application, I encountered some things that were a bit strange compared to iOS development. One of those things is that copy & paste does not automatically work on a NSTextField.

To be exact, copy & paste works on a NSTextField as long as you do not delete the Edit menu from the standard Main menu. If you do that for some reason, you have to implement all the copy & paste functionality yourself.

The key to the implementation is overriding the performKeyEquivalent method and manually checking for cmd + c/v/x/z/a

final class EditableNSTextField: NSTextField {

    private let commandKey = NSEvent.ModifierFlags.command.rawValue
    private let commandShiftKey = NSEvent.ModifierFlags.command.rawValue | NSEvent.ModifierFlags.shift.rawValue

    override func performKeyEquivalent(with event: NSEvent) -> Bool {
        if event.type == NSEvent.EventType.keyDown {
            if (event.modifierFlags.rawValue & NSEvent.ModifierFlags.deviceIndependentFlagsMask.rawValue) == commandKey {
                switch event.charactersIgnoringModifiers! {
                case "x":
                    if NSApp.sendAction(#selector(NSText.cut(_:)), to: nil, from: self) { return true }
                case "c":
                    if NSApp.sendAction(#selector(NSText.copy(_:)), to: nil, from: self) { return true }
                case "v":
                    if NSApp.sendAction(#selector(NSText.paste(_:)), to: nil, from: self) { return true }
                case "z":
                    if NSApp.sendAction(Selector(("undo:")), to: nil, from: self) { return true }
                case "a":
                    if NSApp.sendAction(#selector(NSResponder.selectAll(_:)), to: nil, from: self) { return true }
            } else if (event.modifierFlags.rawValue & NSEvent.ModifierFlags.deviceIndependentFlagsMask.rawValue) == commandShiftKey {
                if event.charactersIgnoringModifiers == "Z" {
                    if NSApp.sendAction(Selector(("redo:")), to: nil, from: self) { return true }
        return super.performKeyEquivalent(with: event)
macos  swift 

Why is there a "lag" in iOS gesture detection near the edges of the screen?

A few weeks ago I encountered a strange problem when working on an iOS application that seemed really strange at first sight.

The task

The task was simple. In the chat detail screen I had to add a voice recording button next to the message input. When the user started holding the button (.touchDown) the voice recording should have started, releasing the button (.touchUpInside) should have finish the recording and sliding to side (.touchDragOutside) should have canceled the voice recording.

The message input was located in the bottom right corner of the screen when keyboard was not shown

and moved up with the keyboard

The problem

When the keyboard was visible the quick recording button worked as expected, but when the quick recording button was at the bottom of the screen there was a “lag”, a delay of about 1 second, between touching the button and the .touchDown even firing.

[Read More]
swift  ios  xcode 

Animating tab bar buttons on tap

If you use the Twitter app on iOS you might have noticed that tapping the buttons in the tab bar makes them bounces.

This is a very subtle animation that I really like so I decided to do the same for the tab bar in the app I currently work on.

The first step is to find the right place to insert the bounce animation into. When you tap a button in the tab bar, the tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) method of the UITabBarController gets called. You cannot override this method in extension, so you have to create a new subclass.

This method gives you the selected UITabBarItem but you need to get to the actual view and its image. I found out that the tab bar contains (at least in my case) a background subview and then subviews corresponding to the tab bar buttons, so when a tab bar button at index N is tapped, its subview is at N+1.

class AnimatedTabBarController: UITabBarController {

    override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
        // find index if the selected tab bar item, then find the corresponding view and get its image, the view position is offset by 1 because the first item is the background (at least in this case)
        guard let idx = tabBar.items?.index(of: item), tabBar.subviews.count > idx + 1, let imageView = tabBar.subviews[idx + 1].subviews.compactMap ({ $0 as? UIImageView }).first else {

        // animate the imageView

To create a bounce animation we can use CAKeyframeAnimation and animate the transform.scale key path. Basically, you need to make the image bigger, then slightly smaller and original size again. This is the animation I use

[Read More]
swift  ios  xcode