はじめに
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 |
---|---|---|
![]() |
![]() |
![]() |
※前回の画像の使い回しなのでちょいバランス悪いですが... | ||
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