頭と尻尾はくれてやる!

パソコンおやじの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



NSSegmentedControlで選択時の画像を白くする

NSSegmentedControlで文字を設定した場合
↑macOSのNSSegmentedControlで各項目に文字列を設定すると選択したところの文字の色は白になってくれる。

NSSegmentedControlで画像を設定した場合(1)
↑ところが文字ではなく画像を設定すると選択、非選択とも元の画像そのままだ。
青い背景だと選択時のが見にくい。できれば文字のように白くなった方が視認性が高い。
Xcodeで選択、非選択それぞれの画像を指定できるのか?と探すもなさそう。

なので自作した。
画像を白黒両方準備するのが面倒なので黒画像のみ作成しておき、白画像は黒画像を反転(※1)させたのを使用する。
クリック時にそれぞれの画像を選択、非選択で適切な画像を設定する、というのを完成させた。

ところがその後で気付いた。
元々それ用のコードが用意されてたことに。
NSSegmentedControlクラスではなくNSImageクラスのプロパティにisTemplateというのがある。
これを真にしておくだけ。
for index in 0..<segmentedControl.segmentCount {
segmentedControl.image(forSegment: index)?.isTemplate = true
}
↑あらかじめ全ての項目に設定しておくと

NSSegmentedControlで画像を設定した場合(2)
↑それだけで選択時には白くしてくれる。
あー、時間を無駄にした、、、



参考ページ
※1 Swift:左右反転した画像,白黒画像,ネガポジ反転画像を生成する - Qiita



Appプレビュー用動画がアップロードできなかった

iOSアプリの申請時にアップするAppプレビュー用の動画(movファイル)がなぜか受け付けてくれないという状況に陥った。

エラーメッセージ

「FILE_RESAVE_CORRUPTED」
「1つ以上のAppプレビューのオーディオがサポートされていないか、破損しています。」
などとエラー表示されるのである。

ただ全てがダメなわけではなかった。
App Store Connectの申請ではiPhone用(6.5インチ、5.5インチ)、iPad用(2つあるが同じでも可)の3パターンが必要でそれぞれ2種類、合計6つの動画を用意してたのだが、半分は期待通りにアップロードできていたのだ。
なので、何が違うんだろうな?と色々と調べてた。

Appプレビューの仕様

↑これはAppプレビューの仕様の一部だが、オーディオに関し1トラック(ステレオ2チャンネル)もしくは2トラックでそれぞれLとRでステレオとある。

アップロードに失敗した動画を確認するとどうやらトラックが2つある。おそらく1トラックにステレオ2チャンネル。「おそらく」としたのはトラックの中身が何チャンネルまでか確認できなかったので。
だとしたら要求仕様から外れてたのでそりゃアップロードはできないだろう。

そもそも今回のAppプレビュー用動画は現在作成中のmacOSアプリで出力したものなのだが、動画ごとにトラックを用意していたために複数の動画から作成する場合はトラックが複数になっていた。
なので強引に1つのトラックになるようにして動画を出力したところApp Store Connectでアップロードできるようになった。




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