以前下記のような記事を書いた。
永遠ログ:[Swift2]NSCalendarUnitの合成
Swiftは頻繁に文法が変わり、APIの変更もあるのだがこの回りはどんどん変わっている。Swift3ではほとんど書いていないので分からないのだが、Swift4で同じ事をしようとしたらまた変わっていた。Swift3でも同じようだ。
Dateから年・月・日などの数字だけを抜き出すときのこと。Objective-C時代は NSCalendarUnit だったが、それがSwift2では NSDateComonents を使うようになっていた。Swift4は Calendar.Compontent を使う。
例えば、ある日付データから何日かを取得するには以下を使う。
続きを読む
2018年07月29日
2017年04月10日
[Mac,Swift2]NSTableViewでソートする
NSTableViewで列を指定して並べ替える方法は、Objective-Cでは簡単に実装することができた。NSArray/NSMutableArrayがNSSortDescriptorを使うメソッドを以て致し、NSArrayが保持しているオブジェクトがNSObjectを親クラスに持っていればkey-value対応により適切に応答してくれた。Mac/iOSのObjective-Cでは全てのオブジェクトはNSObjectを親クラスに持つ。
例えばこんな感じ。
※ここでは以下のようなインスタンス変数の宣言がされているとする。
ところが、Swiftの配列はNSSortDescriptorを引数に持つメソッドがない。ということは自分で実装しなければならない。
続きを読む
例えばこんな感じ。
- (void)tableView:(NSTableView *) aTableView
sortDescriptorsDidChange:(NSArray *) oldDescriptors
{
[mainList sortUsingDescriptors:[mainListView sortDescriptors]];
[mainListView reloadData];
}
※ここでは以下のようなインスタンス変数の宣言がされているとする。
IBOutlet NSTableView *mainListView;
NSMutableArray *mainList;
ところが、Swiftの配列はNSSortDescriptorを引数に持つメソッドがない。ということは自分で実装しなければならない。
続きを読む
2017年03月21日
[Swift2]NSCalendarUnitの合成
NSDateから年・月・日などの数字だけを抜き出すときにNSDateComonentsを使うんだけど、Swiftでちょっと書き方が変わった部分がある。
Objective-C時代はこんな書き方でした。リファレンスからの抜粋。
unsigned unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit;
NSDate *date = [NSDate date];
NSDateComponents *comps = [gregorian components:unitFlags fromDate:date];
これがSiwft2では以下のようになるとリファレンスには書かれている。これだとObjective-Cとさほど代わりはない。
let flags: NSCalendarUnit = .DayCalendarUnit | .MonthCalendarUnit | .YearCalendarUnit
let date = NSDate()
let components = gregorian.components(flags, fromDate: date)
ところがこれを Xcode 7.2.1 で書くと、エラーになってしまってコンパイルできない。
Type of expression is ambiguous without more context
型が分からないというなら直接的に指定するしかないと指定する。
let flags2: NSCalendarUnit = NSCalendarUnit.Day | NSCalendarUnit.Month | NSCalendarUnit.Year
が、するとまた別のエラーになる。
Binary operator '|' cannot be applied to two 'NSCalendarUnit' operands'
じゃあ、一体どう指定するんだよ?と強引に指定するとこんな風になる。
let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)
let flags = NSCalendarUnit(rawValue: NSCalendarUnit.Year.rawValue
+ NSCalendarUnit.Month.rawValue
+ NSCalendarUnit.Day.rawValue
)
guard let comps = calendar?.components(flags, fromDate: date) else {
return
}
let year = comps.year, month = comps.month, day = comps.day
確かにこれだと正常に実行できるんだけど、あまりにも強引すぎてもっときれいな書き方があっていいはずなんだけど。
Swift3ではこんな書き方になる(クラス名等はSwift2のまま)。
let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)
guard let comps = calendar?.components([.Year,.Month,.Day], fromDate: date) else {
return
}
そしてこれがまたコンパイルが通って正常に実行される。Swift3ではメソッドがSetで定義されているのでこれでもいいのだが、Swift2ではcomponents(flags:NSCalendarUnit, fromDate: NSDate)とNSCalendarUnitで定義されているので[NSCalendarUnit]で渡して渡せる理由がわからない。
そこでこんな書き方をしてみたら、ようやくなぜだかわかった。
let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)
let flags = NSCalendarUnit([.Year,.Month,.Day])
guard let comps = calendar?.components(flags, fromDate: date) else {
return
}
リファレンスとかからはわからないけどNSCalendarUnitのコンストラクタに配列を取るものがあるってことなんでしょう。
2017年03月20日
[macOS, Swift2] ファイル選択ダイアログ
未だに古い環境のOS Xでプログラミングしてるんだけど、その中の基本的な操作であるファイルオープンダイアログの使い方を一応覚え書きとして。
NSOpenPanelはObjective-C時代そのまま。インスタンスはそのままコンストラクタで作る。シートでダイアログを開いて、結果はブロックで受け取る。OK/Cancelは NSFileHandlingPanelOKButton / NSFileHandlingPanelCancelButton で判定。
以下はNSViewControllerのサブクラス中で使う場合。
シートを表示している最中だからselfがなくなることはないと思うけど、一応ブロック中でselfを使う場合は習慣化させたいので weak self 。
ファイルを1つだけ選択する場合。NSURLで受け取るのが現代的。複数選択する場合やディレクトリ可の場合には beginSheet... の前に下記コードを入れておくだけ。
NSOpenPanelはObjective-C時代そのまま。インスタンスはそのままコンストラクタで作る。シートでダイアログを開いて、結果はブロックで受け取る。OK/Cancelは NSFileHandlingPanelOKButton / NSFileHandlingPanelCancelButton で判定。
以下はNSViewControllerのサブクラス中で使う場合。
func showOpenPanel() {
let panel = NSOpenPanel()
panel.beginSheetModalForWindow(self.view.window!) {
[weak self] (response) -> Void in
switch ( response ) {
case NSFileHandlingPanelOKButton:
let urls = panel.URLs
self?.readFromURL(urls[0])
break
default:
return
}
}
}
シートを表示している最中だからselfがなくなることはないと思うけど、一応ブロック中でselfを使う場合は習慣化させたいので weak self 。
ファイルを1つだけ選択する場合。NSURLで受け取るのが現代的。複数選択する場合やディレクトリ可の場合には beginSheet... の前に下記コードを入れておくだけ。
self.allowsMultipleSelection = true
self.canChooseDirectories = true
2016年03月24日
Swift: conformProtocol()、respondsToSelector:
Objective-Cでプロトコルに対応しているかどうかを判定するconformProtocol関数や、メソッドを持っているかどうかを調べるrespondsToSelectorのSwiftでの代替について。
ただし、Objective-Cとは違いSwiftでは静的な型の決定を行っているのでほとんどがコンパイル時に決定でき、動的に判定することはない。というかそのような使い方は避けるべきと考えられる。
ここではごく基本的なものを示す。個別の事情によりまた別の判定方法を使った方がよいことがあるので、それらはいつか実際の事例毎にまとめたい。
続きを読む

