Chapters

Hide chapters

UIKit Apprentice

Third Edition · iOS 18 · Swift 5.10 · Xcode 16

My Locations

Section 3: 11 chapters
Show chapters Hide chapters

Store Search

Section 4: 13 chapters
Show chapters Hide chapters

38. Polish the Pop-up
Written by Fahim Farook

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

The Detail pop-up is working well — you can display information for the selected search result, show the image for the item, show pricing information, and allow the user to access the iTunes product page for the item. You are done with the Detail pop-up and can move on to the next item, right?

Well, not quite … There are still a few things you can do to make the Detail pop-up more polished and user friendly.

This chapter will cover the following:

  • Dynamic type: Add support for dynamic type so that your text can display at a size specified by the user.
  • Gradients in the background: Add a gradient background to make the Detail pop-up background look more polished.
  • Animation!: Add transition animations so that your pop-up enters, and exits, the screen with some flair!

Dynamic Type

The iOS Settings app has an accessibility option — under Accessibility ▸ Display & Text Size ▸ Larger Text — that allows users to choose larger or smaller text. This is especially helpful for people who don’t have 20/20 vision — probably most of the population — and for whom the default font is too hard to read. Nobody likes squinting at their device!

You can find this setting both in your device and in the Simulator:

The Larger Text accessibility settings
The Larger Text accessibility settings

Apps have to opt-in to use this Dynamic Type feature. Instead of choosing a specific font for your text labels, you have to use one of the built-in dynamic text styles.

Configure for Dynamic Type

To provide a better user experience for all users, whether their eyesight is good or bad, you’ll change the Detail pop-up to use Dynamic Type for its labels.

Changing the font to the dynamic Headline style
Tnabdils hmo jayn zu wsu djbajej Joodjegu hhqvo

Test Dynamic Type

➤ Close the app and open the Settings app. Go to Accessibility ▸ Display & Text Size ▸ Larger Text. Toggle Larger Accessibility Sizes to on and drag the slider all the way to the right. That gives you the maximum font size — it’s huge!

The pop-up at different text sizes
Zho kan-ol ex rahcabiyf ripg cukan

Changing the pop-up view

When you use Auto Layout, the height of a UI element can be determined by the Auto Layout constraints of its child elements.

Dgu zeurqc am hdi wem-ip luur iq hikakbutij rp bmu kagmtmuayyd

The pop-up view is too tall for the screen
Dcu dar-et seap oq die deqy cud qya fhyouk

The pop-up now shows all the content
Cya let-ar mat ykipd ikz zpi cunwovj

Tweaks

You’ll notice that the price button looks rather puny amongst all the other items. That’s because you didn’t change the font for the price button to be Dynamic Type compliant.

Gradients in the background

As you can see in the previous screenshots, the table view in the background is dimmed by the view of the DetailViewController, which is semi-transparent. That allows the pop-up to stand out more.

The GradientView class

➤ Add a new empty file to the project and name it GradientView.

import UIKit

class GradientView: UIView {
  override init(frame: CGRect) {
    super.init(frame: frame)
    backgroundColor = UIColor.clear
  }

  required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    backgroundColor = UIColor.clear
  }

  override func draw(_ rect: CGRect) {
    // 1
    let traits = UITraitCollection.current
    let color: CGFloat = traits.userInterfaceStyle == .light ? 0.314 : 1
    // 2
    let components: [CGFloat] = [
      color, color, color, 0.2,
      color, color, color, 0.4,
      color, color, color, 0.6,
      color, color, color, 1
    ]
    let locations: [CGFloat] = [ 0, 0.5, 0.75, 1 ]
    // 3
    let colorSpace = CGColorSpaceCreateDeviceRGB()
    let gradient = CGGradient(
      colorSpace: colorSpace,
      colorComponents: components,
      locations: locations,
      count: 4)
    // 4
    let x = bounds.midX
    let y = bounds.midY
    let centerPoint = CGPoint(x: x, y: y)
    let radius = max(x, y)
    // 5
    let context = UIGraphicsGetCurrentContext()
    context?.drawRadialGradient(
      gradient!,
      startCenter: centerPoint,
      startRadius: 0,
      endCenter: centerPoint,
      endRadius: radius,
      options: .drawsAfterEndLocation)
  }
}

Use GradientView

Putting this new GradientView class to work is pretty easy. You’ll add it to your DetailViewController. However, the DetailViewController already has a background color and that will interfere with how the gradient looks/displays.

// Gradient view
view.backgroundColor = UIColor.clear
let dimmingView = GradientView(frame: CGRect.zero)
dimmingView.frame = view.bounds
view.insertSubview(dimmingView, at: 0)
The background behind the pop-up now has a gradient
Snu jaxytfeogc nulicq rvi mov-ul jix baz o hnafiuwl

Animation!

The pop-up itself looks good already, but the way it enters the screen — sliding in from the bottom — is fairly standard. Let’s jazz things up a bit!

The animation controller class

➤ Add another empty file to the project and name it BounceAnimationController.

import UIKit

class BounceAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
  func transitionDuration(
    using transitionContext: UIViewControllerContextTransitioning?
  ) -> TimeInterval {
    return 0.4
  }

  func animateTransition(
    using transitionContext: UIViewControllerContextTransitioning
  ) {
    if let toViewController = transitionContext.viewController(
      forKey: UITransitionContextViewControllerKey.to),
      let toView = transitionContext.view(
        forKey: UITransitionContextViewKey.to) {
      let containerView = transitionContext.containerView
      toView.frame = transitionContext.finalFrame(
        for: toViewController)
      containerView.addSubview(toView)
      toView.transform = CGAffineTransform(scaleX: 0.7, y: 0.7)

      UIView.animateKeyframes(
        withDuration: transitionDuration(
          using: transitionContext),
        delay: 0,
        options: .calculationModeCubic,
        animations: {
          UIView.addKeyframe(
            withRelativeStartTime: 0.0,
            relativeDuration: 0.334) {
              toView.transform = CGAffineTransform(
                scaleX: 1.2, y: 1.2)
          }
          UIView.addKeyframe(
            withRelativeStartTime: 0.334,
            relativeDuration: 0.333) {
              toView.transform = CGAffineTransform(
                scaleX: 0.9, y: 0.9)
          }
          UIView.addKeyframe(
            withRelativeStartTime: 0.666,
            relativeDuration: 0.333) {
              toView.transform = CGAffineTransform(
                scaleX: 1.0, y: 1.0)
          }
        }, completion: { finished in
          transitionContext.completeTransition(finished)
        })
    }
  }
}

Use the new animation controller

To use this animation in your app, you have to tell the app to use the new animation controller when presenting the Detail pop-up.

extension DetailViewController: UIViewControllerTransitioningDelegate {
  func animationController(
    forPresented presented: UIViewController,
    presenting: UIViewController,
    source: UIViewController
  ) -> UIViewControllerAnimatedTransitioning? {
    return BounceAnimationController()
  }
}
required init?(coder aDecoder: NSCoder) {
  super.init(coder: aDecoder)
  transitioningDelegate = self
}

Animate the pop-up exit

After tapping the Close button, the pop-up slides off the screen, like modal screens always do. Let’s make this a bit more exciting and make it slide up instead of down. For that you need another animation controller.

import UIKit

class SlideOutAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
  func transitionDuration(
    using transitionContext: UIViewControllerContextTransitioning?
  ) -> TimeInterval {
    return 0.3
  }

  func animateTransition(
    using transitionContext: UIViewControllerContextTransitioning
  ) {
    if let fromView = transitionContext.view(
      forKey: UITransitionContextViewKey.from) {
      let containerView = transitionContext.containerView
      let time = transitionDuration(using: transitionContext)
      UIView.animate(
        withDuration: time,
        animations: {
        fromView.center.y -= containerView.bounds.size.height
        fromView.transform = CGAffineTransform(
          scaleX: 0.5, y: 0.5)
        }, completion: { finished in
        transitionContext.completeTransition(finished)
        }
      )
    }
  }
}
func animationController(
  forDismissed dismissed: UIViewController
) -> UIViewControllerAnimatedTransitioning? {
  return SlideOutAnimationController()
}
Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2024 Kodeco Inc.

You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now