面白きことは良きことなり

拙く未熟なiOSエンジニアの備忘録と戯言

Mac AppでWindowからfileやdirectoryを選択してpathを取得するやり方

Mac Appの開発へ乗り出し

iTunesで曲を管理しているが、不便に感じていることがあるため、その辺の利便性をなんとかしたいと思い、Mac Appの開発を気ままに進めることにした。

ひとまずやりたいこと

  • directoryを参照して、その配下にあるfile(subdirectory配下も含む)を列挙

コードと自分向けへの説明

下記のWindowを表示し、そこからdirectoryを選択させたい

f:id:aryzae:20180504152845p:plain

  • NSOpenPanelのbeginメソッドで目的のWindowは表示できる
  • NSOpenPanelはPropetyによって、file選択、複数選択等を切り替えられる
@IBAction func openPanel(_ sender: NSButton) {
    let panel = NSOpenPanel()
    // fileを選択可能にするかどうか。defaultは、true 
    panel.canChooseFiles = false
    // 複数選択可能にするかどうか。defaultは、false
    panel.allowsMultipleSelection = false
    // directoryを選択可能にするかどうか。defaultは、false
    panel.canChooseDirectories = true
    // 開始
    panel.begin { response in
        // OK以外に`cancel`, `abort`, `stop`, `continue`があるが、`OK`, `cancel`以外は異常系
        guard response == .OK else { return }
        // closure内でのみ選択したdirectoryのpathが取れる
        // また、panelの持つurlは、file protocol(file://)がついている 
        self.readItems(url: panel.url)
    }
}

NSOpenPanelで選択したdirectory配下のfileを列挙する

  • 先ほどのコードでdirectoryのpathまでは取得できるようになったので、ファイルを列挙する
  • FileManagerのenumeratorメソッドで列挙可能
  • enumeratorincludingPropertiesForKeysで、列挙するfileの各種propertyも取捨できるのかと思ったが、後続のresourceValuesの時に指定したら取れているのでObj-C時代は必要だったが、今は不要?
  • enumeratoroptionsで、隠しファイルやsubdirectory配下も含めるかを指定可能
  • enumeratorのclosure部分はerror handlerなので、必要に応じて書く
func readItems(url: URL?) {
    guard let url = url else { return }
    guard let directoryPath = URL(string: url.path) else { return }
    // includingPropertiesForKeysの意味あるのか?
    let enumerator = FileManager.default.enumerator(at: directoryPath,
                                                    includingPropertiesForKeys: [.isDirectoryKey],
                                                    options: [.skipsHiddenFiles, .skipsSubdirectoryDescendants]) { (url, error) -> Bool in
        // closuerは、errorハンドルのみ
        print(error)
        // trueならerrorでも継続、falseなら停止
        return true
    }
    // 列挙
    for element in enumerator! {
        guard let url = element as? URL else { return }
        print(url)
        let resourceValue = try! url.resourceValues(forKeys: [.isDirectoryKey, .nameKey, .isHiddenKey])
        // forKeysで指定していないものは、nilになる
        print(resourceValue.isDirectory)
        print(resourceValue.name)
        print(resourceValue.isHidden)
        print(resourceValue.isPackage)
        print(resourceValue.localizedName)
    }
}

03/18(日)に突発的に見知らぬ場所へ行きたくなり、宮城へ行った(後編)

キツネ村を出た後…

30分くらいあれば回りきれる園内を3時間程堪能したところで、駅へ戻るバスの時間になったので退園した。

これを逃すと500円の乗車賃で済むところが、タクシーを呼んで数倍のお金を払うことになるので…。

で、バスで行きの時に乗った場所、白石城跡へ戻ったところで12時過ぎということもあり、昼ご飯を食べれるところを探し始めた。新幹線で1駅、地方沿線でも数駅で仙台駅まで行けることもあり、とりあえず仙台に電車で向かいつつ、何を食べるかと次どこに行くかを検索した。

初 仙台

13時になるころに仙台駅に着いた。

f:id:aryzae:20180404221619j:plain

お腹もかなり減っており、ガッツリ地方名産品食べるしかないなってことで、ありきたりながら牛タンを利休でいただいた。

f:id:aryzae:20180404221638j:plain

仙台港も近いので、新鮮な魚類を寿司で食いたい気持ちもあったが、それは夜に食べることにした。

仙台うみの杜水族館

次に観光する場所は色々調べたけど、電車しか足がない自分には選択肢が結構限られていた。元来生き物が好きな節があったので、「仙台うみの杜水族館」の名前を見ても聞いたことなかったし、ピンと来なかったが、逆にどんな魚類が飼育されているか気になって向かった。

f:id:aryzae:20180404221850j:plain

記憶違いかもしれないけど、2011/03/11の時に津波で生物がほぼ全滅した場所だったか?(調べたところ若干違い、「マリンピア松島水族館」というのがあったが、津波と老朽化の影響で2015年に閉館し、その後意志を継いで出来たのがこの水族館らしい。)

水族館で魚メインだけど他の生物も…

入ってすぐの場所にでかい水槽に多様の魚類が一緒くたに存在していてなかなか圧巻だった。

youtu.be

アジやらイカやら食卓で馴染みのある魚類からマンボウやらオオカミウオダイオウグソクムシ等テーマがあるのかよくわからないけど、多くの生物がいた。

f:id:aryzae:20180404223120j:plain

海水生物以外にも淡水生物でアリゲーターガーピラルク、コイ、イワナとかもいたりする。そしてなぜかいるメガネカイマン。

f:id:aryzae:20180404223548j:plain

魚介類、爬虫類以外だと、ツメナシカワウソや各種ペンギン、アメリカンビーバーもいる。

youtu.be

f:id:aryzae:20180404224048j:plain

カワウソだとコツメカワウソが割とネットで見かけてもてはやされているが、ツメナシカワウソは数倍でかくて驚いた。そして飼育員のお姉さんが可愛かった。


自分が滞在時間中に、イルカのショー?をやっていたみたいだけど、個人的にイルカは興味の対象外+子連れの家族も多く空いて見れるチャンスと館内を巡っていた。

大きい方ではないが、展示されている種類が豊富だったこともあって、回りきる頃には夕方になっていた。

時間に余裕あれば、温泉でも浸かってから帰ることも考えていたが、突発的に遠征したため、この日が日曜日ということもあり翌日の仕事を考えて帰路につくことにした。

ずんだと寿司

夕飯は寿司の気分で完全に決まっていたので、行ければ漁港付近で寿司屋へと思っていた。しかし、水族館の最寄駅から帰り道と港は逆方向だったので、諦めた。

諦めたというより、港から仙台駅まで遠くないから、駅前でも鮮度良い寿司ネタは十分あるだろうと判断し、調べたところ"鮨勘"というお店が自分の懐事情に合いつつ美味いネタが食べれそうだったので、向かった。

時間も時間なだけに、寿司は持ち帰りで、あとは駅構内で売っていたずんだシェイクを携え新幹線へ乗り込んだ。

f:id:aryzae:20180404225154j:plain

f:id:aryzae:20180404225200j:plain

ずんだと寿司の食い合わせはちょっとアレだが、どちらも美味しかった。

特に寿司はシャリ少なめネタ厚めで満足度が高く、また食べたいくらいだ。

突発的な日帰り旅行を行なったが、気ままで縛られない快適な旅だった。次どこいくか等のリサーチだけは時間かかるため、最低限食べ物と観光地、あとその場所の地理関係くらいはざっくり把握しておいたほうが、より効率的に移動や観光できたかもしれない。

まぁたまには非効率で無駄と思える時間を過ごすのも悪くはないとも思う。

次はGWあたりどこかいけたらなーと思う。観光客で混雑するから、GW明けとかでいけたら最高かもな。

03/18(日)に突発的に見知らぬ場所へ行きたくなり、宮城へ行った(前編)

ことの始まり

03/18(日)、数日前からふとした衝動でぶらりどこか適当な遠くへ行きたいという気持ちに駆られ、ノープラン気味にどこか行こうと思い立った。

で、どこ行こうか考えた結果、動物ではキツネと猛禽類が好きな自分としては、以前に掛川花鳥園でフクロウを堪能したので、国内唯一の蔵王キツネ村に行って見たいと思っていたので、行くことに決めた。残りは本当にノープランで朝1に出て新幹線に乗って宮城県白石蔵王駅に向かった。

f:id:aryzae:20180403011508j:plain

キツネ村入場

白石城跡から蔵王キツネ村まで試験的にバスが運行されており、その試験期間最終日だったのでタクシーで向かわずに済んだのは良かった。(タクシーだと3000〜4000円かかるらしい)

バスの乗る乗客は全部で12〜3人くらいだったが、うち10人くらいは中国人の観光客で、こういうややマイナーな行き先にも来るぐらいだからどこ行ってもいるんだろうなー。

20分かからないくらいだったか、バスで移動し、無事蔵王キツネ村に到着。こちらが入り口…

f:id:aryzae:20180403011821j:plain

……

こちらが入り口。

f:id:aryzae:20180403011832j:plain

意外に自家用車で来てたりタクシーで来てる人も多かった。蔵王といえばスキー場や樹氷でも有名だし、近くには温泉もあるので、そこからの流れで来やすいのもあるのかな。

入場料は大人1000円。まぁ良心的?

チケットを買うときに注意事項として受付のおばちゃんから説明を受ける。曰く「キツネが放たれてる檻に人間が入って行くことになる。近づいて来てもしゃがんだり、手を出さないでください。噛まれて怪我をします。また、しゃがんでスマホやカメラを構えると噛んで壊したりいたずらすることがあります。園内歩いていると後ろからキツネが近づいて来ることがあります。そのときは目を合わせてください。」のようなことを曖昧な記憶ながら言われた。

キツネとの戯れ

ざっくりおおきく分けて園内は3つのスペースがある。

  1. 檻の中に飼われていて、キツネの種類が明記されているエリア
  2. キツネが柵で囲われた広いスペースで放し飼いされており、餌やり(有料)ができるエリア
  3. キツネ以外のなぜかいるモルモットやうさぎ等のエリア
1. 檻の中に飼われていて、キツネの種類が明記されているエリア

f:id:aryzae:20180403012938j:plain

f:id:aryzae:20180403012949j:plain

f:id:aryzae:20180403013010j:plain

2. キツネが柵で囲われた広いスペースで放し飼いされており、餌やり(有料)ができるエリア

f:id:aryzae:20180403013117j:plain

f:id:aryzae:20180403013127j:plain

日差しが気持ちいいぐらいで寝てるやつらばかりだったけど、耳だけこちらに向けてやや警戒してるやつもいたりで可愛かった。園内に100頭以上のキツネがいるらしく、所狭しとそこらじゅうにキツネがいて、時には喧嘩してる場面に遭遇する。

3. キツネ以外のなぜかいるモルモットやうさぎ等のエリア

割愛


1日に1〜2回キツネを抱っこできる時間が設けてあり、自分が滞在中もあったので、なかなか来ることないしせっかくだからと抱っこしてきた(600円)

f:id:aryzae:20180403013811j:plain

蛍光色のは、雨合羽で抱っこしてる時に糞尿することがあるために着せられる。尿はとても臭いらしい。

一部のキツネは抱っこ中にジッとしてるのが嫌なのか、抱っこしている人間が嫌なのか暴れるのもいた。自分が抱っこしたのは、なんか顔からして悟ってる感じで微動だにしなかった。

キツネ村滞在の感想

狭い檻の中に閉じ込められるようにしてただ寝て過ごすキツネがいたり、放し飼いエリアで自由に過ごしているものの餌やりばで餌の取り合いによる喧嘩で激しくもみくちゃに暴れたりしていて、キツネ達から自然界にいるよりもストレスがかかっているように感じられた。

自然界のキツネを詳細に知ってるわけじゃないけど、金稼ぎの道具としてキツネがいいように扱われてるように見える部分があり、気持ちが引く印象を受ける部分もあった。

キツネの中には喧嘩で怪我をして首元がぱっくり裂けて肉が見えてるやつもいたり、頭に怪我の跡が残ってるやつもいた。

f:id:aryzae:20180403014802j:plain

キツネはやっぱり好きだと思うけど、キツネ村に再度行きたいかと聞かれたら即答にしくいくらいには闇を感じた。

また、同じ観光客がキツネが喧嘩してるのを見てキャッキャいいながら笑ってたり、無邪気にかわいい〜って声を上げてるのにも寒気がした。


あぁ自宅でキツネ飼いたい…。 庭とかあるならホンドキツネでもアカキツネでもシロキツネでもいいし、室内ならフェネックとか飼いたい…でもフェネックお高い…。

いつかコキンメフクロウとフェネックを一緒に飼って共演させたいなぁ…(下手したらどっちかが捕食しそうで怖いからできない気もするけど)

03/18(日)に突発的に見知らぬ場所へ行きたくなり、宮城へ行った(後編)へ続く

R.swiftとWARNで競合したので解消した

WARNの説明

Obj-C時代は、#warningを書いて意図的に警告を表示し、後から対応する等のメモがわりに使用していた。(他にそういう人は多いはず)

で、これがSwiftになると使えないので、// WARN:を書いたら同じように警告を表示するようにRun Scriptを入れるわけです。

qiita.com

R.swiftとWARNの競合

便利なR.swiftを利用している人もそれなりにいると思う。

そこで、先ほどの// WARN:のRun Scriptを入れてやったら、自分の環境で競合してerrorがでた。

競合の理由としては、R.swiftが生成するSwiftファイル格納用に作成したフォルダ名R.swiftが、WARNの警告検索対象である*.swiftにひっかかったため。

下記のように除外項目-not -name "R.swift"を入れてやり解消してやった。

KEYWORDS="WARN:"
find ${SRCROOT} \( -name "*.h" -or -name "*.m" -or -name "*.swift" -not -name "R.swift" \) -print0 | \
xargs -0 egrep --with-filename --line-number --only-matching "($KEYWORDS).*\$" | \
perl -p -e "s/($KEYWORDS)/ warning: \$1/"

iPadのiPhone Appの互換表示モードについて

iOS9が切れたら3.5inchも切れると勘違いしてる人が多い

iOS9系が切れれば、iPhone 4sが最後だから3.5inch端末をサポートしなくてすむ!と喜んでいる輩は多いだろう。

だが、そうはApple(問屋)が卸さん!!

qiita.com

何故か、日本のAppleのレビューする人はiPadiPhoneアプリをチェックすることがしばしば見かける(体感的に)

で、画面崩れていようものならRejectくらうわけですよ。

iPadの互換表示は一律同じではない

ちなみにiPadは、全機種が3.5inchでiPhoneアプリを互換表示するわけではない。

以下が、iPadと互換表示のサイズ

機種 互換サイズ
iPad 2 3.5inch
iPad Air 3.5inch
iPad Air 2 3.5inch
iPad Pro 9.7inch 3.5inch
iPad Pro 10.5inch 3.5inch
iPad Pro 12.9inch 4.7inch