頭と尻尾はくれてやる!

パソコンおやじのiPhoneアプリ・サイト作成・運営日記


NSTableViewであるセルを編集できないようにする

macOSのNSTableViewで特定のあるセル1つだけは編集できないように設定しようとしてかなり苦労した。
storyboard(xibファイル)側でNSTableViewを設置して表示内容はNSArrayController使ってバインディングさせてる。
なお、このtableはscroll viewの下の方に位置しており、最初は表示されていない。

let tableCellView = tableView.view(atColumn: c, 
row: r,
makeIfNecessary: true) as? NSTableCellView
↑このようにしてNSTableViewオブジェクトから指定したいrow, columnのNSTableCellViewオブジェクトを取得し、tableCellView.textFieldからNSTextFieldオブジェクトが得られるので
textField.isEditable = false
とすればいけそうなもんだが、どうもうまくいかない。

使えそうなのをNSTableViewDataSourceやNSTableViewDelegateなどで探し、ようやく
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView?
というNSTableViewDelegateメソッドにたどり着いた。
    func tableView(_ tableView: NSTableView, 
viewFor tableColumn: NSTableColumn?,
row: Int) -> NSView? {
let tableCellView = tableView.makeView(withIdentifier: tableColumn!.identifier,
owner: self) as? NSTableCellView
if tableColumn!.identifier.rawValue == “someID" {
let isEditable: Bool = (row != r)
tableCellView!.textField!.isEditable = isEditable
}

return tableCellView
}
↑columnをidentifierで特定し、指定したrowだけは編集できなくする、というものだが、、、
これでもうまくいかない。

なんでかなー?と調べたり試行錯誤してようやく気付いた。
storyboard側での設定で、セルにあるNSTextFieldのBehavior設定で

Xcodeの設定

↑ここをEditableにすると、コードでどういう方法で頑張ってあるセルだけは編集不可と指定してもEditableになってしまう。
このBehaviorをNoneにしておいてあとはコードで上記のように指定すればいけた!
長かったわ、、、



macOS 10.15.6 Catalina
Xcode 11.7
Swift 5.2.4
スポンサーサイト






NSSecureCodingでオプショナル型の保存、読み込み方法

iOS/macOSアプリでデータを保存する時に、データが色々あるとそのデータを持つクラスを作って、そのクラスがNSSecureCodingプロトコルに従うようにすることが多い。
保存の方法がUserDefaultsだろうがNSKeyedArchiverやFileWrapperなどを使おうがまあいい。ともかくクラスがNSSecureCodingに従ってる場合の話。

このプロトコルに従うとクラスはこんな感じになると思う。
class Person: NSObject, NSSecureCoding {

var name: String?
var weight: Float?

static var supportsSecureCoding = true

// save
func encode(with coder: NSCoder) {

coder.encode(name, forKey: "name")
coder.encode(weight, forKey: "weight")
}

// load
required init(coder: NSCoder) {

super.init()
name = coder.decodeObject(forKey: "name") as? String
weight = coder.decodeFloat(forKey: "weight”)
}
}
これを実行するとname: String?についてはいけるが、weight: Float?について読み込み時に
*** -[NSKeyedUnarchiver decodeDoubleForKey:]: value for key (weight) is not a 64-bit float
なんてエラーが出たりする。
まだこのエラーが出るような場合はまだマシで、ビット数を間違えてデータをメモリに展開するのかアプリが起動時に全然関係ないところで「このデータがおかしいで!」と落ちてしまう、という症状が出てた。

結局のところオプショナル型を扱う場合には
//weight = coder.decodeFloat(forKey: "weight”)
weight = coder.decodeObject(forKey: "weight”) as? Float
のようにしてやるといいみたい。
どうしてもcoder.decodeFloatを使いたいなら保存時に
coder.encode(weight!, forKey: "weight")
のようにunwrapすれば可能。もちろん変数がnilではないことが前提だけど。

func decodeFloat(forKey key: String) -> Float
確かにリファレンスだと↑このように返り値はFloatなのでnilだと返せないよな。



macOS 10.15.6 Catalina
Xcode 11.7
Swift 5.2.4


NSTableViewでセルの編集をしたい時にハマったこと

macOSのNSTableViewを使う時、最近はdelegateとかdataSourceとか使わずにバインディングを使ってる。その方が楽だからだ。

macOSでバインドを使いテーブルを表示する
↑基本はこんな感じ。

今回、このようなNSArrayControllerを使って表示しているテーブル内のセルの値を直接編集しようとしてハマったところをメモ。

NSTextFieldの設定

1. セル(NSTextField)のBehavior(上の図)をEditableにする。

2. セルの入力(編集)完了を拾いたい場合にはAction(上の図)をSent On End Editingとしておき、
Sent Actionsで自分で設定したメソッドに接続しておく(下の図)。
Sent Actionsの設定
かつ、tableViewのdelegateを接続しておく(なぜかセルのNSTextFieldのdelegateではない!)。

3. xibファイルにNSTableViewを設置したらtableのセル(のNSTextField)にはデフォではNumberFormatterは設定されてない。
NumberFormatterを後から設定する場合、
Number Formatterの追加方法
↑この部分をドラッグしてセルにドロップすればいけた。

4. セルのバインディングの設定で参照する変数の型がStringならいいけどIntやFloatだと
Property cannot be marked @objc because its type cannot be represented in Objective-C
となってしまう。なのでNSNumber型にした。
厳密には当方の都合によりNSNumber?型にしてる

5. 上記のNSNumber?で値がまだnilの場合にはplaceholderを表示させたい。
placeholderの指定場所
↑この場合、NSTextFieldのではなく(なんでや?!)、セルのバインディングの設定ペインの”Null Placeholder”に記述するといけた。

NSTableView表示結果
↑placeholder表示した様子。


NSTableViewはいつもながら苦労するけど、SwiftUIになったらこんなの一撃なんかなあと妄想。



macOS 10.15.6 Catalina
Xcode 11.7
Swift 5.2.4



  TopPage  



Copyright ©頭と尻尾はくれてやる!. Powered by FC2 Blog. Template by eriraha.