LoginSignup
21
8

More than 3 years have passed since last update.

iOS13でもNavigationBarButtonの位置を調整したい

Posted at

はじめに

iOS11でNavigationBarButtonにStackViewが入ってきた影響で、private view を操作する方法を以前Qiitaに書きましたが、iOS13から private view を操作すると怒られる(Crash)ようです。
ちなみに以下のErrorが出ます。

Client error attempting to change layout margins of a private view

なので、Appleに怒られないように操作する方法をご紹介します。

以下は以前の記事です。この記事は以下の改良版的な立ち位置になります。

iOS11からNavigationBarButton周りの仕様が変わったせいで、UIBarButtonItemの位置調整が上手くいかなくなった - Qiita
https://qiita.com/rd0501/items/2309e14c5a3f365867ec

先に結果をまとめると

Tap範囲もちゃんとしたい場合は両端8ptより外側にBarButtonを配置するのは無理でした

参考: UIBarButtonItem & iOS 11
https://www.matrixprojects.net/p/uibarbuttonitem-ios11#alignment-rect-insets

一応、各OSでのView Hierarchy を確認

iOS13 iOS11 iOS10
スクリーンショット 2019-09-25 14.57.48.png スクリーンショット 2018-03-19 15.12.28.png スクリーンショット 2018-03-19 15.06.17.png

※前回の画像の使い回しなのでちょいバランス悪いですが...
View Hierarchyを見た感じだと、iOS11とiOS13はほぼ同じですね。
違いは private なクラス使用しているかどうかぐらいでしょうか。

対応方法

本題です。
先に言うと上でも引用した以下の記事のまとめ的な感じです。

UIBarButtonItem & iOS 11
https://www.matrixprojects.net/p/uibarbuttonitem-ios11/

概要

1. alignmentRectInsetsを操作できるView等を用意します

2. navigationItem.left or rightBarButtonItems の firstItem に、幅8ptの .fixedSpace の UIBarButtonItem を入れる

3. 追加するUIBarButtonItemを生成するが、alignmentRectInsetsを操作して、spacer(.fixedSpace)の分だけずらす(leftBarButtonItem左に8pt, rightBarButtonItem右に8pt)

alignmentRectInsetsを操作できるView等を用意します。

// 以下のような、後からalignmentRectInsetsを操作できるView等を用意します。

class CustomView: UIView {
    var alignmentRectInsetsOverride: UIEdgeInsets?
    override var alignmentRectInsets: UIEdgeInsets {
        return alignmentRectInsetsOverride ?? super.alignmentRectInsets
    }
}

class CustomButton: UIButton {
    var alignmentRectInsetsOverride: UIEdgeInsets?
    override var alignmentRectInsets: UIEdgeInsets {
        return alignmentRectInsetsOverride ?? super.alignmentRectInsets
    }
}

navigationItem.left or rightBarButtonItems の firstItem に、幅8ptの .fixedSpace の UIBarButtonItem を入れる

// UIViewController の extension などに以下のような処理を追加して、 UIBarButtonItem を Set するときは以下の setNavLeftButton と setNavRightButton のメソッドを使用するようにする

extension UIViewController {

    func setNavLeftButton(_ button: UIBarButtonItem) {
        self.navigationItem.leftBarButtonItems = self.createBarButtonItemsForEachOS(button: button)
    }

    func setNavRightButton(_ button: UIBarButtonItem) {
        self.navigationItem.rightBarButtonItems = self.createBarButtonItemsForEachOS(button: button)
    }

    // iOS11以降の場合はcustom以外のButtonを入れないと外側からの余白が16ptになってしまうので、その対応のために.fixedSpaceを入れている
    // 入れると8ptになる
    private func createBarButtonItemsForEachOS(button: UIBarButtonItem) -> [UIBarButtonItem] {
        if #available(iOS 11, *) {
            return [self.createSpacer(), button]
        } else {
            return [button]
        }
    }

    private func createSpacer() -> UIBarButtonItem {
        let spacer = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
        spacer.width = 8 // なんか0にしても8になるので一旦8で
        return spacer
    }
}

追加するUIBarButtonItemを生成するが、alignmentRectInsetsを操作して、spacer(.fixedSpace)の分だけずらす**(leftBarButtonItem左に8pt, rightBarButtonItem右に8pt)

// 以下のような、処理を行ってUIBarButtonItemを生成して、Right or LeftBarButtonItem に入れます。

extension UIBarButtonItem {

    enum BarButtonItemPosition {
        case right
        case left
    }

    static func createBarButtonItem(position: BarButtonItemPosition) -> UIBarButtonItem {
        let button = CustomButton(frame: CGRect(x:0, y: 0, width: 24, height: 24))
        // spacerが入っている分8pt外側にずらす
        switch position {
        case .left:
            button.alignmentRectInsetsOverride = UIEdgeInsets(top: 0, left: 8, bottom: 0, right: -8)
        case .right:
            button.alignmentRectInsetsOverride = UIEdgeInsets(top: 0, left: -8, bottom: 0, right: 8)
        }
        button.translatesAutoresizingMaskIntoConstraints = false
        return UIBarButtonItem(customView: button)
    }
}

以下、実装例です。

// 使用例
class HogeViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // barItem
        let leftButton = UIBarButtonItem.createBarButtonItem(position: .left)
        let rightButton = UIBarButtonItem.createBarButtonItem(position: .right)
        self.setNavLeftButton(leftButton)
        self.setNavRightButton(rightButton)
    }
}

※上記のコードで一部動かない可能性がありますので、エラーが発生したら修正リクエストください!

参考

UIBarButtonItem & iOS 11
https://www.matrixprojects.net/p/uibarbuttonitem-ios11/

alignmentRectInsets - UIView | Apple Developer Documentation
https://developer.apple.com/documentation/uikit/uiview/1622648-alignmentrectinsets?language=objc

iOS11からNavigationBarButton周りの仕様が変わったせいで、UIBarButtonItemの位置調整が上手くいかなくなった - Qiita
https://qiita.com/rd0501/items/2309e14c5a3f365867ec

21
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
8