Karhin’s Blog
Apps, Design and Music
Later ↑

Ekran 1.2

Ekran is an application that allows you to turn even the most unsuccessful screenshot or screen recording into something you wouldn't be embarrassed to share on social networks or post on your blog.

New devices have been added to the app: the entire iPhone 16 lineup. For these devices, you can now choose the frame color.

The macOS interface has been completely revamped. The app now looks much better and no longer displays in scale mode.

There's now an option to enable audio in videos. Previously, videos were saved without sound, but now you can include it. It's a great feature if you want to showcase content with audio. I’ve personally used it to beautifully present track demos on Instagram.

Some minor bugs and issues have also been fixed.

You can download the latest version from the App Store.

"Natatki" released on streaming platforms

I haven’t released music for several years, so for me, it’s a small miracle that I finally managed to bring together all 12 tracks I worked on throughout 2024 and release such a big project: a full 35 minutes of music.

I’m really happy with how this album turned out. It blends house, classic hip-hop, and ambient music. What I enjoy most is that people with different musical tastes each pick different tracks as their favorites. Almost every track on the album has its own story and atmosphere – the key is to feel it.

All the links to various platforms are gathered here.

How to place a view in the UINavigationBar and increase its height

You can see an expanded UINavigationBar with additional UIViews located below the title in many native Apple applications.

Calendar and Fitness

There is a very simple way to add your own UIView as a pallet in UINavigationBar using private API, which was discovered by @SebJVidal. You can take the source code from the tweet and close the article. Just don't forget to obfuscate the private selectors before the review 😁

But there is another way. In the latest update of Artykul, in the new Links section, I recreated similar behavior without using a private API and at the same time, I didn't have to create my own UINavigationBar from scratch.

Before coming to a solution, I tried making subclasses of UINavigationBar, trying to add UIView directly, and some other things that either don't work or break the smooth transitions between UIViewController. The main conclusion I reached is not to even try to touch the navigation bar.

Sometimes the simplest solutions, which lie right on the surface, are hard to see at first. This is one of them.

The main trick is not to try to extend what cannot be extended by design, but to recreate this behavior. Fortunately, it's fairly simple to do so.

All the source code is available in this repository. I will only focus on a few key points that explain what is happening.

Visual

You need to make the UINavigationBar completely transparent. This is best done through UINavigationBarAppearance, if you don't want to break transparency in the UINavigationController stack.

let appearance = UINavigationBarAppearance()
appearance.configureWithTransparentBackground()

navigationItem.standardAppearance = appearance
navigationItem.scrollEdgeAppearance = appearance
navigationItem.compactAppearance = appearance
navigationItem.compactScrollEdgeAppearance = appearance

Now we can start creating our PalletView. The original naming from the private API looks strange, so let's call it a pallet.

We need to recreate the original border (or shadow) and background blur. The color of the original “shadow” can be obtained from the already familiar UINavigationBarAppearance. The height of this shadowView should be equal to one physical pixel: 1.0 / UIScreen.main.scale.

private let shadowView: UIView = {
    let view = UIView()
    view.translatesAutoresizingMaskIntoConstraints = false
    
    let navigationBarAppearance = UINavigationBarAppearance()
    navigationBarAppearance.configureWithOpaqueBackground()
    view.backgroundColor = navigationBarAppearance.shadowColor
    
    return view
}()

The background is simpler. You can use anything, but to closely match the original UINavigationBar, it's best to use this effect.

let standardVisualEffect = UIBlurEffect(style: .systemThinMaterial)

In the original implementation, it's easy to notice that the shadow and background are hidden when we're at the very top of the UIScrollView. Therefore, it's necessary to provide two methods that will be used to respond to scrolling.

func scrollEdgeAppearance() {
    shadowView.alpha = 0.0
    visualEffectView.effect = nil
}

func standardAppearance() {
    shadowView.alpha = 1.0
    visualEffectView.effect = standardVisualEffect
}

Layout

A UILabel is used as the contentView, but you can replace it with anything you like. The most important aspects are:

  • The visualEffectView should match our PalletView.
  • The shadowView needs to be attached to the bottom of the PalletView.
  • The top anchor of the PalletView should correspond to view.topAnchor.
  • The bottom anchor of the PalletView should correspond to the bottom anchor of the contentView.
  • The top anchor of the contentView should correspond to view.safeAreaLayoutGuide.topAnchor.
// View Controller

palletView.leftAnchor.constraint(equalTo: view.leftAnchor),
palletView.topAnchor.constraint(equalTo: view.topAnchor),
palletView.contentView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
palletView.rightAnchor.constraint(equalTo: view.rightAnchor)
// PalletView

contentView.leftAnchor.constraint(equalTo: safeAreaLayoutGuide.leftAnchor, constant: 0.0),
contentView.rightAnchor.constraint(equalTo: safeAreaLayoutGuide.rightAnchor, constant: 0.0),
self.bottomAnchor.constraint(equalTo: label.bottomAnchor, constant: 8.0)

Since we have PalletView placed on top of a UIScrollView or UICollectionView, don't forget to add an inset to it equal to the height of PalletView minus the safeArea. Also, remember about the scroll indicators.

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    collectionView.contentInset.top = palletView.frame.height - view.safeAreaInsets.top
    collectionView.verticalScrollIndicatorInsets.top = collectionView.contentInset.top
}

Scroll

Now, all that's left is to animate the palletView. Here, we'll need the two methods we added earlier. Just remember to add the height of palletView to the contentOffset, since we manipulated the inset.

public func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let scrollContentOffset = scrollView.contentOffset.y + palletView.frame.height
    if scrollContentOffset > 0.0 {
        UIView.animate(withDuration: 0.100, animations: {
            self.palletView.standardAppearance()
        })
    } else {
        UIView.animate(withDuration: 0.100, animations: {
            self.palletView.scrollEdgeAppearance()
        })
    }
}

There is still some room for improvement here. In the original UINavigationBar implementation, the animation is not done through a timer, but rather based on the scroll value within a very small range (approximately 4 points). So, the code should look something like this.

public func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let scrollContentOffset = scrollView.contentOffset.y + palletView.frame.height
    if scrollContentOffset < 0.0 {
        self.palletView.setVisibilityPercentage(to: 0.0)
    } else if scrollContentOffset < 4.0 {
        self.palletView.setVisibilityPercentage(to: scrollContentOffset / 4.0)
    } else {
        self.palletView.setVisibilityPercentage(to: 1.0)
    }
}

You can try implementing this yourself, but it will require diving into the wonderful world of UIVisualEffectView if you plan to use the blur effect, but that's a whole different story 😁

Result

The entire source code with an example is available in the repository.

Don't forget to follow me on X/Twitter and Telegram.

Ekran is available on the App Store

Ekran is our new application that allows you to turn even the most unsuccessful screenshot or screen recording into something you wouldn't be embarrassed to share on social networks or post on your blog.

How much time has been wasted manually fitting screenshots to center them perfectly within the content? How many minutes are spent finding the right device frame when preparing a post or images for websites? And how much time must be dedicated to doing this in a video editor, dealing with masks and worrying about pixel-perfect placement?

Now, not even a minute is needed, thanks to Ekran. We've made the app as straightforward as possible. And it's already available for iOS, iPadOS, and macOS. Moreover, there are no unpleasant subscriptions involved. We've created it as if it were for ourselves.

I'm sorry, I've always wanted to do this, throughout the development process 😅

The main features:

  • Videos, and everything operates super fast without even heating up the phone, even on very old iPhone models.
  • Cross-platform support. Everything as we like it.
  • Hiding of banking cards, phone numbers, and email addresses.
  • A hundred ways to insert an image into the application, to make it as easy as possible.
  • Automatic balancing of screenshots.
  • Margins, padding, shadows, rounded corners, fun backgrounds, and that's all.

And most importantly, there are no subscriptions, and practically all features are free. If you want more, it's just a one-time payment of $20. And that covers all three applications for all your favorite devices. We've looked at the competitors; try to find something like it.

The application can be downloaded from the App Store.

3 Ways to Play Xbox on a Mac

There are a thousand and one reasons why this might be useful: the TV is occupied or you don't have one at all, poor eyesight or something else. In any case, Macs do have good displays, but unfortunately, there is no simple way to connect a console directly.

Remote Play

As of the end of 2023, Microsoft still doesn't have an official Remote Play app for macOS, but they do have one for iPad and iPhone. I don't understand why they haven't ported it yet: there's no objective reason.

Fortunately, there are third-party apps, such as OneCast. I've used it for several months. Currently, it costs about $30 for a one-time purchase.

It works roughly the same way as Remote Play in the official app. Sometimes you connect once and see Full HD with almost no lag, and sometimes you're watching a pixelated slideshow. I never quite understood how it works under the hood and how it's related to the phases of the moon, but I did manage to play several games on it.

HDMI Converter

There's this magical thing called an "HDMI Capture Card.” It's a device that connects to the computer via Type-C and converts the HDMI signal into something that the computer recognizes as a webcam.

Such devices range from $10 to $50. I'll admit I made a mistake and ordered an adapter from Sandberg, which converts to Full HD at 30Hz (funny that it's cheaper than OneCast). Theoretically, you can find devices with better specs, but they might be a bit more expensive.

To play, I use OBS. I tried with QuickTime, VLC, and some other apps, but they created noticeable lag. In an ideal world, this should have been the perfect solution, but it has its drawbacks.

  • The picture quality and stability immediately outshine Remote Play.
  • The adapter creates a slight delay that you need to get used to. It doesn't interfere with playing games like Cyberpunk or Starfield.
  • Pay close attention to the output image specifications, not like I did.
  • This setup works for iPads with a Type-C port (iOS 17 or later) and an app like Orion.

Portable Display?

Okay, it seems playing Xbox via a Mac isn't a great idea. Another option: buy a portable display. I've seen 13 and 15-inch portable IPS Full HD displays on the market. They're priced similarly to a budget Samsung TV, are space-efficient, and weigh around 800 grams.

In short, the best option: get rich, buy a cinema, and play Xbox there instead of dealing with these alternatives. Don't forget to subscribe to my Telegram channel. I'd skip Twitter since Musk might soon ruin it completely.

Earlier ↓