Closed18

【Swift】UIToolbar 0->1

yoshitakayoshitaka
var toolbar: UIToolbar!

The custom toolbar associated with the navigation controller.

func setToolbarHidden(Bool, animated: Bool)

Changes the visibility of the navigation controller’s built-in toolbar.

var isToolbarHidden: Bool

A Boolean indicating whether the navigation controller’s built-in toolbar is visible.

class let hideShowBarDuration: CGFloat

This variable specifies the duration when animating the navigation bar. Note that this is a constant value, so it cannot be set.

yoshitakayoshitaka

UIViewController

setToolbarItems(_:animated:)

Sets the toolbar items to be displayed along with the view controller.

func setToolbarItems(_ toolbarItems: [UIBarButtonItem]?, 
            animated: Bool)

Parameters

toolbarItems
The toolbar items to display in a built-in toolbar.

animated
If true, animate the change of items in the toolbar.

ナビゲーションコントローラーによって管理されるビューコントローラーは、このメソッドを使用して、ナビゲーションコントローラーの組み込みツールバーのツールバー項目を指定できます。 ビューコントローラが表示される前、またはすでに表示された後に、ビューコントローラのツールバー項目を設定できます。

yoshitakayoshitaka

UIBarButtonItem

A specialized button for placement on a toolbar or tab bar.

class UIBarButtonItem : UIBarItem

通常、Interface Builderを使用して、バーボタンアイテムを作成および構成します。 ただし、セッターメッセージをUIBarButtonItemAppearanceに送信してすべてのボタンをカスタマイズするか、特定のUIBarButtonItemインスタンスに送信することで、ボタンの外観をカスタマイズできます。 UINavigationItemオブジェクト(backBarButtonItem、leftBarButtonItem、rightBarButtonItem)またはUIToolbarインスタンスの標準的な場所でカスタマイズされたボタンを使用できます。

一般に、カスタム値が設定されていない他の状態で使用できるように、通常の状態の値を指定する必要があります。 同様に、プロパティがバーのメトリックに依存している場合(たとえば、iPhoneの場合、横向きの場合、バーの高さは標準とは異なります)、UIBarMetrics.defaultの値を指定する必要があります。

外観と動作の構成の詳細については、ツールバーを参照してください。

init(image: UIImage?, style: UIBarButtonItem.Style, target: Any?, action: Selector?)

Initializes a new item using the specified image and other properties.

UIBarButtonItem.Style

enum Style : Int
case plain = 0
タップすると光ります。 デフォルトのアイテムスタイル。
case done = 2
完了ボタンのスタイル-たとえば、いくつかのタスクを完了して前のビューに戻るボタン。
func setBackgroundImage(UIImage?, for: UIControl.State, barMetrics: UIBarMetrics)

Sets the background image for a specified state and bar metrics.
※ボタンのバックグラウンドだからうまくいかない?

backgroundImage

The background image for the specified state and metrics.

state

A control state.

barMetrics

Bar metrics.

良い結果を得るには、backgroundImageはストレッチ可能な画像である必要があります。

UIBarMetrics

Constants to specify metrics to use for appearance.

enum UIBarMetrics : Int
case `default` = 0
case compact = 1
電話イディオムを使用するときのメトリックを指定します。
case defaultPrompt = 101
UINavigationBarUISearchBarなどのpromptプロパティを持つバーのデバイスのデフォルトメトリックを指定します。
case compactPrompt = 102
UINavigationBarUISearchBarなど、電話イディオムを使用するときに、promptプロパティを使用してバーのメトリックを指定します。

メトリック(英:metric)とは
ネットワークの世界における「目的地まで、あと○メートルです」のこと。行われる道案内(ルーティング)で使われる情報で、相手のところに辿り着くまでどれだけ大変かの指標

flexibleSpace()

Creates a new flexible width space item.

class func flexibleSpace() -> Self

柔軟な幅のスペースUIBarButtonItem。
これはiOS 14.0+

UIBarButtonItem.SystemItem

Defines system-supplied images for bar button items.

case flexibleSpace
Blank space to add between other items. The space is distributed equally between the other items. Other item properties are ignored when this value is set.
case fixedSpace
Blank space to add between other items. Only the width property is used when this value is set.

これでイケた

let spaceFlexToolbar = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let spaceToolbar = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
spaceToolbar.width = 200
yoshitakayoshitaka

ちょっと気になる

init(image: UIImage?, landscapeImagePhone: UIImage?, style: UIBarButtonItem.Style, target: Any?, action: Selector?)

Initializes a new item using the specified images and other properties.

yoshitakayoshitaka

Legacy Customizations

Customize appearance information directly on the navigation bar object.

iOS 13以降では、standardAppearance、compactAppearance、およびscrollEdgeAppearanceプロパティを使用してナビゲーションバーをカスタマイズします。 これらのレガシーアクセサーを引き続き使用して、ナビゲーションバーの外観を直接カスタマイズできますが、さまざまなバー構成の外観を自分で更新する必要があります。

func setBackgroundImage(UIImage?, for: UIBarPosition, barMetrics: UIBarMetrics)

Sets the background image to use for a given bar position and set of metrics.

yoshitakayoshitaka

最終的に使えそうなやつは

override func viewDidLoad() {
        super.viewDidLoad()
        self.navigationItem.rightBarButtonItem = self.editButtonItem
        self.navigationItem.rightBarButtonItem?.title = "選択"
        let sendButton = UIBarButtonItem(barButtonSystemItem: .bookmarks, target: nil, action: nil)
        let saveButton = UIBarButtonItem(barButtonSystemItem: .camera, target: nil, action: nil)
        let spaceFlexToolbar = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
        let spaceToolbar = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
        spaceToolbar.width = 200
        self.setToolbarItems([spaceFlexToolbar, sendButton, spaceToolbar, saveButton, spaceFlexToolbar], animated: true)
        self.navigationController?.setToolbarHidden(true, animated: true)
    }
    
    override func setEditing(_ editing: Bool, animated: Bool) {
        super.setEditing(editing, animated: animated)
        if self.isEditing {
            self.editButtonItem.title = "選択"
            self.navigationController?.setToolbarHidden(false, animated: true)
        } else {
            self.editButtonItem.title = "完了"
            self.navigationController?.setToolbarHidden(true, animated: true)
        }
}
yoshitakayoshitaka
var toolbar: UIToolbar! { get }

このプロパティには、ナビゲーションコントローラーによって管理される組み込みツールバーへの参照が含まれています。 このツールバーへのアクセスは、ツールバーからアクションシートを提示したいクライアントにのみ提供されます。 UIToolbarオブジェクトを直接変更しないでください。

このツールバーのコンテンツの管理は、このナビゲーションコントローラーに関連付けられたカスタムビューコントローラーを介して行われます。 ナビゲーションスタック上のビューコントローラーごとに、UIViewControllerのsetToolbarItems(_:animated :)メソッドを使用して、ツールバーアイテムのカスタムセットを割り当てることができます。

このツールバーの表示は、isToolbarHiddenプロパティによって制御されます。 ツールバーは、現在表示されているView ControllerのhidesBottomBarWhenPushedプロパティにも従い、必要に応じて自動的に非表示および表示します。

これカスタムできたら使いたいけど(viewControllerのプロパティとも連動しているし)直接変更できないらしい。

yoshitakayoshitaka

レガシーカスタマイズ

  • バーのスタイルを設定する
    var barStyle: UIBarStyle
    外観を指定するナビゲーションバーのスタイル。
    enum UIBarStyle
    さまざまなタイプのビューのスタイルの外観を定義します。
  • タイトルの設定
    var titleTextAttributes: [NSAttributedString.Key : Any]?
    バーのタイトルテキストの属性を表示します。
    func titleVerticalPositionAdjustment(for: UIBarMetrics) -> CGFloat
    指定されたバーメトリックのタイトルの垂直位置調整を返します。
    func setTitleVerticalPositionAdjustment(CGFloat, for: UIBarMetrics)
    指定されたバーメトリックのタイトルの垂直位置調整を設定します。
  • バーボタンアイテムの構成
    var tintColor: UIColor!
    ナビゲーションアイテムとバーボタンアイテムに適用する色合いの色。
  • 戻るボタンの設定
    var backIndicatorImage: UIImage?
    戻るボタンの横に表示されている画像。
    var backIndicatorTransitionMaskImage: UIImage?
    プッシュおよびポップ遷移中にコンテンツのマスクとして使用される画像。
  • 背景を変える
    var barTintColor: UIColor?
    ナビゲーションバーの背景に適用する色合いの色。
    func backgroundImage(for: UIBarMetrics) -> UIImage?
    指定されたバーメトリックの背景画像を返します。
    func setBackgroundImage(UIImage?, for: UIBarMetrics)
    指定されたバーメトリックの背景画像を設定します。
    func backgroundImage(for: UIBarPosition, barMetrics: UIBarMetrics) -> UIImage?
    指定されたバーの位置と一連の指標に使用する背景画像を返します。
    func setBackgroundImage(UIImage?, for: UIBarPosition, barMetrics: UIBarMetrics)
    特定のバーの位置と一連の指標に使用する背景画像を設定します。
  • 影を追加する
    var shadowImage: UIImage?
    ナビゲーションバーに使用する影の画像。
yoshitakayoshitaka

カスタマイズに高さが存在しないため、navbarではできない。toolbarを作っておくしかない

myToolbar.frame = CGRect(x: myToolbar.frame.origin.x, y: myToolbar.frame.origin.y, width: myToolbar.frame.size.width, height: 20)

ものによってはこれが効くかもしれないが、navbarはダメだった。

カスタムバーなら効きそう

toolbar.frame = CGRect(x: toolbar.frame.origin.x, y: toolbar.frame.origin.y, width: toolbar.frame.size.width, height: 300)

これは効くけど、これならinit時に指定するし、あとでframe=する必要はないかな

yoshitakayoshitaka

UIViewの.translatesAutoresizingMaskIntoConstraintsがあると表示ができない?

ビューの自動サイズ変更マスクを自動レイアウト制約に変換するかどうかを決定するブール値。

var translatesAutoresizingMaskIntoConstraints: Bool { get set }

このプロパティの値がtrueの場合、システムは、ビューの自動サイズ変更マスクで指定された動作を複製する一連の制約を作成します。 これにより、ビューのフレーム、境界、または中央のプロパティを使用してビューのサイズと場所を変更し、自動レイアウト内に静的なフレームベースのレイアウトを作成することもできます。

自動サイズ変更マスクの制約により、ビューのサイズと位置が完全に指定されることに注意してください。 したがって、競合を発生させずにこのサイズまたは位置を変更するための制約を追加することはできません。 自動レイアウトを使用してビューのサイズと位置を動的に計算する場合は、このプロパティをfalseに設定してから、ビューにあいまいで競合しない一連の制約を指定する必要があります。

デフォルトでは、プログラムで作成するすべてのビューでプロパティがtrueに設定されています。 Interface Builderでビューを追加すると、システムはこのプロパティを自動的にfalseに設定します。

上をfalseにした場合は、

```swift
NSLayoutConstraint.activate([
            toolbar.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor),
            toolbar.leadingAnchor.constraint(equalTo:view.safeAreaLayoutGuide.leadingAnchor),
            toolbar.trailingAnchor.constraint(equalTo:view.safeAreaLayoutGuide.trailingAnchor),
            toolbar.heightAnchor.constraint(equalToConstant: 50)
])

これを追加しないと、表示されない・・・
iphoneのsafeAreaありなしに対応するならこれが必要そう

いや、これはまずい。☝️がないとスクロールされてしますツールバーになる。。。。

yoshitakayoshitaka

わかったこと

  • ボタンサイズよりもバーの高さが優先される
  • frameのy=0は一番上になる
let footerBarHeight: CGFloat = 50
        
        // ツールバーのインスタンス化
        let toolbar = UIToolbar(frame: CGRect(
                                    x: 0,
                                    y: self.view.bounds.size.height - 120,
                                    width: self.view.bounds.size.width,
                                    height: footerBarHeight)
        )

これでやっと上手く表示されたけど、機種によって変わるので要注意

つまり、NSLayoutConstraint.activateこれ使った方が絶対良い

この辺りはもうちょい掘り下げたい

yoshitakayoshitaka
func setBackgroundImage(UIImage?, for: UIBarMetrics)

これがなぜかきかない。。。。

navigationBarはきく。toorbarがきかない。
setShadowImageは.bottomにしたらきいた。

一旦諦め

let image = UIImage(systemName: "pencil")
self.navigationController?.toolbar.setBackgroundImage(image, forToolbarPosition: .bottom, barMetrics: .defaultPrompt)
self.navigationController?.toolbar.setShadowImage(image, forToolbarPosition: .bottom)
self.navigationController?.toolbar.backgroundImage(forToolbarPosition: .topAttached, barMetrics: .default)
self.navigationController?.navigationBar.setBackgroundImage(image, for: .default)

ここでいう、ツールバーのバックグラウンドイメージだけは一切きいてくれる様子がなかったので、これは無理っぽいので、ボタンの背景で上手くできないか

yoshitakayoshitaka

これでボタン作ると上手くいかないことがあるので要注意!!

let sendButton = UIButton(frame: CGRect(x: 0, y: 0, width: 300, height: 50))
sendButton.setTitle("送信", for: .normal)

多分サイズを指定したくてこうしたんだろうが、どっちにしろボタンのサイズはタブバーの高さで制限かかるので、無視で良さそう。それよりも

let sendButtonItem = UIBarButtonItem(title: "送信", style: .plain, target: self, action: nil)
sendButtonItem.tintColor = UIColor.black
sendButtonItem.setBackgroundImage(UIImage.colorImage(color: UIColor.blue, size: CGSize(width: 150, height: 40)), for: .normal, barMetrics: .default)

これで作れば良さそう。

ちなみに、UIImageはextensionしている

extension UIImage {
    class func colorImage(color: UIColor, size: CGSize) -> UIImage {
        UIGraphicsBeginImageContext(size)
 
        let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: size)
        guard let context = UIGraphicsGetCurrentContext() else { fatalError("")}
        context.setFillColor(color.cgColor)
        context.fill(rect)
        guard let image = UIGraphicsGetImageFromCurrentImageContext() else { fatalError("")}
        UIGraphicsEndImageContext()
 
        return image
    }
}

https://qiita.com/koyoarai_/items/9bfaa8c1175727fcdf7d

let sendButtonItem = UIBarButtonItem(title: "送信", style: .plain, target: self, action: nil)
sendButtonItem.tintColor = UIColor.white
sendButtonItem.setBackgroundImage(UIImage.colorImage(color: UIColor.green, size: CGSize(width: 180, height: 80)), for: .normal, barMetrics: .default)

しかしこれだとハジがキツくなるし、そもそもサイズをしてしてバックグラウンドイメージにする時点で無理があると理解。

yoshitakayoshitaka
var isTranslucent: Bool { get set }

ツールバーが半透明の場合は、View ControllerのedgesForExtendedLayoutプロパティとextendedLayoutIncludesOpaqueBarsプロパティを構成して、ツールバーの下にコンテンツを表示します。

ツールバーにカスタムの背景画像がない場合、または背景画像のいずれかのピクセルのアルファ値が1.0未満の場合、このプロパティのデフォルト値はtrueです。 背景画像が完全に不透明な場合、このプロパティのデフォルト値はfalseです。 このプロパティをtrueに設定し、カスタム背景画像が完全に不透明である場合、UIKitは1.0未満のシステム定義の不透明度を画像に適用します。 このプロパティをfalseに設定し、背景画像が不透明でない場合、UIKitは不透明な背景を追加します。

yoshitakayoshitaka

ツールバーむずい

toolbar.isTranslucent = false

これもきかない。色の問題かと思ったけど、全く違った。

        toolbar.translatesAutoresizingMaskIntoConstraints = false
        toolbar.isTranslucent = false
        toolbar.barTintColor = UIColor(red: 0, green: 0.53, blue: 0.1, alpha: 1.0)
        toolbar.barStyle = .default
        
        let sendButton = UIButton(frame: CGRect(x: 0, y: 0, width: 200, height: 80))
        sendButton.setImage(UIImage(systemName: "paperplane.fill"), for: .normal)
        sendButton.tintColor = UIColor.white
        let sendButtonItem = UIBarButtonItem(customView: sendButton)
        let savebutton = UIButton(frame: CGRect(x: 0, y: 0, width: 200, height: 80))
        savebutton.setImage(UIImage(systemName: "folder.fill"), for: .normal)
        savebutton.tintColor = UIColor.white
        let saveButtonItem = UIBarButtonItem(customView: savebutton)
        let spaceFlexToolbar = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
        let spaceToolbar = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
        spaceToolbar.width = 10
        
        toolbar.setItems([spaceFlexToolbar, sendButtonItem, spaceToolbar, saveButtonItem, spaceFlexToolbar], animated: true)
        
        self.view.addSubview(toolbar)
        
        NSLayoutConstraint.activate([
                    toolbar.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
                    toolbar.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
                    toolbar.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
                    toolbar.heightAnchor.constraint(equalToConstant: 70)
        ])

ひとまず、この問題はこれで進めていく。

yoshitakayoshitaka

処理も追加して、これで完成

  let sendButton = UIButton(frame: CGRect(x: 0, y: 0, width: 200, height: 80))
        sendButton.setImage(R.image.reload_off(), for: .normal)
        sendButton.tintColor = UIColor.white
        sendButton.addTarget(self, action: #selector(sendData), for: .touchUpInside)
        let sendButtonItem = UIBarButtonItem(customView: sendButton)
        let savebutton = UIButton(frame: CGRect(x: 0, y: 0, width: 200, height: 80))
        savebutton.setImage(R.image.setting_off(), for: .normal)
        savebutton.tintColor = UIColor.white
        savebutton.addTarget(self, action: #selector(deleteData), for: .touchUpInside)
        let saveButtonItem = UIBarButtonItem(customView: savebutton)
        let spaceFlexToolbar = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
        let spaceToolbar = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
        spaceToolbar.width = 100

        toolbar.setItems([spaceFlexToolbar, sendButtonItem, spaceToolbar, saveButtonItem, spaceFlexToolbar], animated: true)
yoshitakayoshitaka

いけた!!!!!

customToolbar.setBackgroundImage(UIImage(systemName: "person.crop.square"), forToolbarPosition: .any, barMetrics: .default)

position をanyにして、barMetrics .defaultらいけたみたい!
positionが .top / .topAttached だとダメで、.bottomはいけた。
barMetricsが.compact / .compactPrompt / .defaultPrompt だとダメだった。

ちなみに、

self.navigationController?.navigationBar.setBackgroundImage(UIImage(systemName: "person.crop.square"), for: .any, barMetrics: .default)

もいけたので、ツールバーもいけそう!
こっちも
positionが .bottom / .topAttached だとダメで、.topはいけた。
barMetricsが.compact / .compactPrompt / .defaultPrompt だとダメだった。

結果わかったことは、.anyと.defaultにしておけば良さそう!

このスクラップは2021/04/28にクローズされました