Thursday, May 04, 2017

Xcode and Swift Tips and Tricks

During my development of my iOS app, I have encountered some hurdles. Even though some of them are small ones, I did spend a lots of time to find solution to overcome them. Here are some in my past short period.

Can't find customized class from Storyboard

Normally customized view controller classes are used to handle specific requirement for some views. In Storyboard, this customized class can be specified in Storyboard right property panel "Show the identity inspector" tab.

For whatever reason, I may pressed space bar by chance in the area of Module. This caused the Module's value as None! As a result, I got this run time error:

Could not cast value of type 'UIViewController' (0x113f1b798) to 'MyProject.MyViewController' (0x113985200).

I spent hours trying to figure out why. Eventually, I found out the simple solution: change the module to my project.

This is what I had in my Storyboard for this customized class setting:

Hide Tabbar and use toolbar

In a tabar driven app, sometimes, I may need to hide tabbar and show a toolbar as alternative. To hide tabar for next view controller, it can be done in prepare(for segue: sender:) event.

  1. override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
  2.  ...
  3.  let vc = segue.destination
  4.  // call vc's hidebottombar
  5.  vc.hidesBottomBarWhenPushed = true
  6.  ...
  7. }

For the case of normal view controller, bar button item can be directly added to the bottom of the view. For the case of tableview controller, in order to show bar button item on the bottom, a toolbar has to be used to encapsulate bar button items. This can be done in storyboard.

However, it seems that there is no API or method to let vc to show or hide toolbar. At first, I found a solution to show or hide toolbar by adjusting toolbar y position in viewWillAppear event. The problem is that if user changes device from portrait to landscape, the toolbar will be off screen, disappeared. As a result, I have to adjust toolbar y position in the event of viewRotated.

To adjust toolbar y position, I add the following API to view controller as extension. This makes it easier in call this API to show toolbar.

  1. extension UIViewController {
  2.  func setToolBarShow(show: Bool, delay: Double = 2) {
  3.    if let toolbar = self.navigationController?.toolbar {
  4.      let newOriginY = UIScreen.main.bounds.height - toolbar.bounds.size.height
  5.      if toolbar.frame.origin.y != newOriginY {
  6.        toolbar.frame.origin.y = newOriginY
  7.      }
  8.      if delay > 0 {
  9.        // Make sure origin is set correctly
  10.        DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
  11.          if toolbar.frame.origin.y != newOriginY {
  12.            toolbar.frame.origin.y = newOriginY
  13.          }
  14.        }
  15.      }
  16.    }
  17.  }
  18. ...
  19. }

Here the trick to show toolbar in the events of viewDidAppeared and viewRotated is to delay adjusting toolbar y position. It seems that in both events, without delay, toolbar would not show. It may be something inside UIKit would not seeing this adjustment. With delay, the toolbar would show correctly at the bottom of the view.

Change navigation back button's title

In the case of parent VC to child VC by using navigation controller, the top-left button is used to pop off child VC and back to parent VC. The title of this button is by default the title of the parent VC. If the parent title is too long, it may make top text too crowded if the title of child VC is too long. One simple way to change top-left button title to "Back". This can be done by clearing parent title before pushing to child VC. UIKit will automatically use "Back" as the title.

Here is the solution to do that: clear view's title when pushing a new view controller. This can be done in the event of prepare() for segue.

One thing to remember is that to set view's title on the event of viewDidAppear(), otherwise, the view's title would be blank if the view is pushed back.