月別アーカイブ: 2019年1月

【Swift4】UITableViewCellにUISwitchをつけたがindexPath.rowを受け取るのが大変だった件。

UITableViewは便利です。そんなに使いこなせていないけど。
Cellを自由にアレンジしたいけどわざわざクラスを作るんはめんどくさいってことで、
StoryboardでCellに直接乗せていろいろやってたんだけど、UISwitchがうまくかない。
具体的にはスイッチのイベントは受け取れるけどそれが何番目のセルがわからない。

同じ悩みが書いてあるところはいろいろと有った。
参考サイト
【今度こそ】カスタムセルに設置したUISwitchのindexPathを取得する – 技術はメシのタネ
Swift – TableViewのカスタムセルに設置したUISwitchのインデックス番号を取得する方法|teratail
iOS – TableViewに貼り付けたスイッチのインデックスを取得する方法|teratail
しかし、どうにもスマートでない感じ。

最終的に参考にさせてもらったのはこちらのサイト。
CustumTableViewCellとaccessoryViewを使ってTableVIewのレイアウトをカスタマイズ | すいすいSwift

accessoryViewを使うのがポイントでした。

//Cellの中身
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath as IndexPath)
    
    //UISwitchをcellのアクセサリービューに追加する
    let switchView = UISwitch()
    cell.accessoryView = switchView
    
    //スイッチの状態
    switchView.isOn = dic["switch"] as! Bool
    //タグの値にindexPath.rowを入れる。
    switchView.tag = indexPath.row
    //スイッチが押されたときの動作
    switchView.addTarget(self, action: #selector(fundlSwitch(_:)), for: UIControlEvents.valueChanged)
    
    return cell
}
//スイッチのテーブルが変更されたときに呼ばれる
func fundlSwitch(_ sender: UISwitch) {
    print(sender.tag)
    print(sender.isOn)
}

インデックス番号がわからないとスチッチつける意味ないですよね。/(^o^)\
うじゃうじゃ。

【Swift4】時刻と分と曜日でアラームの日時を設定する。

アプリのアラーム関係で時刻の取扱は鬼門だ。
なかなかうまく行った試しがない。
頭の中もこんがらがりまくりだ。

ゆーてても仕方がないので先人たちの知恵とXcodeの保管機能を駆使してやってみた。

参考サイトはこちら。
UILocalNotificationでローカル通知を実装する【Swift】 | うるおいらんど
[iOS 10] User Notifications framework を使用して指定日時に発火するローカル通知を作成する #wwdc | DevelopersIO
[Swift] カレンダーや時刻のデータを扱う DateComponents – JoyPlotドキュメント
【Swift】Dateの王道 【日付】 – Qiita

時、分、曜日を指定すると次の曜日のその時刻が出てくる。
playGroundをつかってやってみた。

import UIKit
//目的の時刻と曜日を設定。
let fireHour = 16 //指定の時刻
let fireMinute = 30 //指定の分
let fireWeekday = 2 //曜日の番号。日曜が1、月曜が2,火曜3、水曜4、木曜5、金曜6、土曜7
//今日の日時を取り出す
let date = Date() //今日の日時
let calendar = Calendar.current
let components = calendar.dateComponents([.year , .month ,.day , .hour, .minute, .weekday], from: date) //今日の年月日時分、曜日を取り出す。
let weekday = components.weekday //今日の曜日を取り出す。
let hour = components.hour //現在の時刻
let minute = components.minute //現在の分

var interval = TimeInterval()
//時刻を分に変換
let time = (hour! * 60) +  minute!
let fireTime = (fireHour * 60) +  fireMinute
//現在の曜日と時刻から目的の時刻までの時間を計算
if (weekday! >= fireWeekday && time >= fireTime){
    interval = Double(60 * 60 * 24 * ((7 + fireWeekday) - weekday!))
} else {
    interval = Double(60 * 60 * 24 * (fireWeekday - weekday!))
}

//通知する日時を設定。
let nextDate = date.addingTimeInterval(interval)
var fireDateComponents = calendar.dateComponents([.year , .month ,.day , .weekday], from: nextDate)
fireDateComponents.hour = fireHour
fireDateComponents.minute = fireMinute
fireDateComponents.second = 0

//date型に変換。
let fireDate =  Calendar.current.date(from: fireDateComponents)!

アラームに設定するのはまたこのあとの話…。
うじゃうじゃ。

【Swift4】【Objective-C】UNUserNotificationCenterで通知の許可をもらう。

毎度のことながら自分用の劣化コピー備忘録ブログです。

自分がメインで手を入れているアプリはObjective-Cで作って徐々にSwiftに置き換わっています。
そんな中でlocalNotificationがiOS10以降でdepricatedになりUNUserNotificationCenterを推奨されたので調べてみました。

参考サイト
Xcode|iOS10で新しくなったUNUserNotificationCenterの使い方を調べてみた
ローカルプッシュ通知(iOS10以降) – Qiita
[iOS 10] User Notifications framework を使用してリモート通知を受け取る処理を実装する #wwdc | DevelopersIO

まずUNUserNotificationCenterを使う前提としてUserNotifications.frameworkが必要です。
入れておきましょう。
スクリーンショット 2019-01-27 13.01.20

とりあえず未だ、AppDelegateはObjective-CなんでObjective-Cで書きます。
まずはUserNotificationをインポートしてデリゲートをセットします。

~略~
#import "AppDelegate.h"
@import UserNotifications;

@interface AppDelegate ()<UIApplicationDelegate,UNUserNotificationCenterDelegate> {

~略~

次にUNUserNotificationCenterのユーザーに対する許諾をもらう部分です。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    //バッジ、サウンド、バナーの許可を求める。
    [[UNUserNotificationCenter currentNotificationCenter]
     requestAuthorizationWithOptions:(UNAuthorizationOptionBadge |
                                      UNAuthorizationOptionSound |
                                      UNAuthorizationOptionAlert )
     completionHandler:^(BOOL granted, NSError * _Nullable error) {
      if (granted) {
          // APNSはここで設定するの?
      }
     }];

    [UNUserNotificationCenter currentNotificationCenter].delegate = self;
//    [application registerForRemoteNotifications];//これはリモート通知の場合に要るのかな?

以上でアプリがバックグランドの時はローカル通知を受けれますが、フォアグラウンドでも通知が受けれるようにするには下記のメソッドを追加します。

- (void)userNotificationCenter:(UNUserNotificationCenter *)center
       willPresentNotification:(UNNotification *)notification
         withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler {
    // アプリがフォアグランドにいた場合の通知動作を指定する。
    completionHandler(UNNotificationPresentationOptionSound |
                      UNNotificationPresentationOptionAlert);
};

viewControllerはほぼSwiftに置き換わってるのでObjective-Cでのテストはしていません。
以下は参考サイトからのコピペですすみません。
多分動くと思います。(-人-)

// UserNotificationsのインポートが必要

- (IBAction)push:(id)sender {

    // 通知を作成
    UNMutableNotificationContent *unMutableNotice = [UNMutableNotificationContent new];
    // title、body、soundを設定
    unMutableNotice.title = @"おはようございます";
    unMutableNotice.body = @"今日も一日頑張ってください!";
    unMutableNotice.sound = [UNNotificationSound defaultSound];

    // 通知タイミングを設定(今回は、実装後5秒後に通知を受信します)
    UNTimeIntervalNotificationTrigger *triger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:5 repeats:NO];

    // リクエストの作成
    UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:@"リクエスト" content:unMutableNotice trigger:triger];

    // NUserNotificationCenterにリクエストを投げる
    [UNUserNotificationCenter.currentNotificationCenter addNotificationRequest:request withCompletionHandler:nil];
}

追記。
swiftで通知の許可を貰う場合。
参考サイト
<Swift>iOS 10 User Notifications Framework実装まとめ – Qiita

UserNotifications.frameworkを入れるのは上記と同じ。

で、インポートします。

import UIKit
import UserNotifications //ここ

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate ,UNUserNotificationCenterDelegate { //フォアグラウンドで受け取るにはデリゲートも。

didFinishLaunchingWithOptionsで許可を求めます。

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        //通知許可のRequest。
        let center = UNUserNotificationCenter.current()
        center.requestAuthorization(options: [.badge, .alert, .sound]) { (granted, error) in
            if granted {
                print("Allowed")
            } else {
                print("Didn't allowed")
            }        }

        // フォアグラウンドで受け取るため、UNUserNotificationCenter のデリゲートを設定する
        UNUserNotificationCenter.current().delegate = self
        return true
    }

フォアグラウンドでの通知の受け取りは以下のMethodを実装。

    // アプリがフォアグラウンドの時に通知を受け取る
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                willPresent notification: UNNotification,
                                withCompletionHandler completionHandler: (UNNotificationPresentationOptions) -> Void) {
        completionHandler([.sound ,.alert]) // 通知バナー表示、通知音の再生を指定
    }

テスト用に5秒で鳴る通知。

        //ローカル通知の設定
        let content = UNMutableNotificationContent()
        content.title = "Title"
        content.subtitle = "Subtitle" // 新登場!
        content.body = "Body"
        content.sound = UNNotificationSound.default
        
        // 5秒後に発火
        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
        let request = UNNotificationRequest(identifier: "FiveSecond",
                                            content: content,
                                            trigger: trigger)
        
        // ローカル通知予約
        UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)

上記をviewWillApperあたりに入れると、開くたびに5秒後に鳴るテストが出来ます。\(^o^)/

【Kotlin】R.layout.activity_mainでunresolved referenceというエラー?

Kotlinに手を出しました。\(^o^)/
未だにSwiftもまともに使えてないですが、Androidアプリも作りたいなーってことでボチボチと勉強を始めます。

とりあえず本を一冊買ってきて写経から始めます。(-人-)
買った本はこちら。

[商品価格に関しましては、リンクが作成された時点と現時点で情報が変更されている場合がございます。]

はじめてのAndroidプログラミング 第3版 [ 金田 浩明 ]
価格:2862円(税込、送料無料) (2019/1/13時点)

Android Studioのインストールから初期設定まで良い感じです。
Android StudioはXcodeとはやはり使い勝手がちがって戸惑うところも多いですね。

インストールから、HelloWorldを終わっていよいよ簡単なアプリを作っていくところで本に出てないエラーで引っかかりました。
じゃんけんアプリを作っていて、画像を配置し終わって、コードを書くところです。
上記の本を持ってる人だったら、127ページ辺りです。

コードのViewという部分が赤くなって、android.view.Viewが自動インポートに失敗している場合というのは本に対応策が載っていて対処できたのですが、その上の、R.layout.activity_main のRが赤い字になっていて何らかのエラーを起こしているようでそこで数時間悩んでしまったのでその備忘録です。
こんな感じ。
スクリーンショット 2019-01-13 10.33.58

ぐぐってみるとR.javaがないって事らしい。
でもそれは自動インポートされるもので手動で入れるものでもないらしい。
XMLが間違ってるとR.javaはインポートされないとかなんとか。
MainActivity.ktのコードを書いていてのエラーなので、activity_main.xmlをひらいてテキスト表示でコードで見てもエラーはない。

でいろいろと良く見て調べていくと、activity_main.xmlではなく、activity_result.xmlの方でエラーが見つかった。
スクリーンショット 2019-01-13 10.34.47

画像ボタンの幅や高さの数値の単位を付けていないことが原因らしい。
そのくらい自動で直してくれよと思わんでもないが、そういう仕様ならば仕方がない。
数値にdpをつけて修正。

BuildからClean Projectを選ぶとエラーが無くなった。
スクリーンショット 2019-01-13 10.35.21

Xcodeとはいろいろとお作法が違うので大変だが、それもまた楽し!ということにしておこう。

【Swift4】縦長のスクロール画面をscrollViewとstackViewを使って作ると便利だった。

追記〜〜
Xcoceの仕様が変わったので改訂版書きました。
【Swift5】縦長のスクロール画面をscrollViewとstackViewを使って作ると便利だった。改訂版 | iPhoneアプリ備忘録
〜〜〜〜

設定画面とか縦長でスクロールする画面を作りたい時ってあるじゃないですか。
でもテーブルビュー使うのめんどくさいな、みたいな。
そんなときに便利なやり方です。

一応の完成形はこんな感じ。
zuhqs-9xva4

参考サイト
制約の修正なしで縦方向に要素を追加可能なビューの作成 – Qiita
【iOS】ちょっと待って!その画面UITableViewで作る必要ないかも – ペンギン村 Tech Blog
【UIScrollView】Autolayoutで縦スクロール【Xcode8.x】 – Qiita

ではまず、Safe AreaにScroll Viewを配置します。
Add New Constraintsで上下左右全てのマージンを0とします。
スクリーンショット 2019-01-06 20.41.39

次いで、その中にVertical Stack Viewを配置し、Scroll Viewと同様に、Add New Constraintsで上下左右全てのマージンを0とします。
スクリーンショット 2019-01-06 20.42.11

このままではScroll Viewのコンテントサイズの制約がないためエラーがでます。
スクリーンショット 2019-01-06 20.42.36

マウスの右ボタンでStack ViewからScroll ViewへドラッグしてEqual Widthで横幅を揃える制限をつけます。
スクリーンショット 2019-01-06 20.45.30(2)

Stack Viewの子コンテンツでScroll Viewの高さが決まります。
とりあえずStack ViewのDistributionをEqual SpacingにしてSpacingを0にします。
スクリーンショット 2019-01-06 20.47.18

子コンテンツを入れて高さが決まるとScroll Viewの高さが決まります。
スクリーンショット 2019-01-06 20.49.28

2019年1月22日追記〜〜
このままだと上下の高さがSafe Areaの分ずれるので、Scroll Viewの上下の対象をSafe AreaからSuperviewに変えます。
スクリーンショット 2019-01-22 13.21.40
↓対象をSuperviewに変えて
スクリーンショット 2019-01-22 13.22.02
↓マージンを0にします。
スクリーンショット 2019-01-22 13.24.48
↓上も下もね。
スクリーンショット 2019-01-22 13.25.04
これで上下のズレがなくなります。
追記終わり〜〜
 
 

さて、ここから縦長な画面にします。
操作するViewを選んで、simulated sizeを「freeform」にして、heightを設定します。
とりあえず1200にしてみました。
スクリーンショット 2019-01-06 20.51.30

StackViewに高さが200の子Viewを6個入れて分かりやすように色を付けてみました。
スクリーンショット 2019-01-06 21.04.34

これでアプリを起動すると綺麗にスクロールしました。\(^o^)/
zuhqs-9xva4

これだけのことがコードを一行も書かずにできるってすごいなーと思います。\(^o^)/

【Swift4】NSDictionaryを要素に持つNSMutableArrayをDictionaryのKey順にソート

自分は独学でiPhoneアプリを作っています。
初心者向けのSwiftの本を数冊とあとはインターネットで検索して調べています。
C言語の基礎知識とかはまったくないです。英語のApple Developerなんちゃらを読んでもさっぱり理解できません。
しかし、やりたいことが決まって大体のやりかたはネットで調べれば基本的なことはたいてい出てきますが、
たまにこの人どーやって調べてこのやり方にたどり着いたんだろうと思うことがたまにあります。
今回もそんな感じです。
そして自分はすぐに忘れてしまうし、先人のブログが消えるとわからなくなるのでここに劣化コピーを載せています。

今回は、配列に入ったディクショナリをその要素でソートしたかったんですが、なかなかやり方が分からず難儀しました。
今回の参考サイトはこちら。
[swift] NSDictionaryを要素に持つNSArray/NSMutableArrayをKey順にソート | BlueBear I/O
【Swift4】配列の中身(数値・文字・日付)を比較してソートする方法【Xcode9】 | ニートに憧れるプログラム日記

適当に調べながらプレイグラウンドでコードを書いたので冗長だったりするかも。
サンプルコード

import UIKit

var array = NSMutableArray()
var dic = NSDictionary()
var Date1 = Date()

Date1 = Date.init(timeIntervalSince1970: 0) // 今の時間
dic = ["date":Date1,
       "num":1]
array.add(dic)

var Date2 = Date()
Date2 = Date.init(timeIntervalSince1970: 60) // 1分後
dic = ["date":Date2,
       "num":2]
array.add(dic)

var Date3 = Date()
Date3 = Date.init(timeIntervalSince1970: 60*60) // 1時間後
dic = ["date":Date3,
       "num":3]
array.add(dic)

print(array)

//並び替え
//key順に並び替える、降順はascending:をtrue,昇順の場合はfalseとする
let sort_descriptor:NSSortDescriptor = NSSortDescriptor(key:"date", ascending:false)
array.sort(using: [sort_descriptor])

print(array)

ほんとこーゆーのってどうやってしらべてるんだろう?
とりあえずなんとかなってよかった。
うじゃうじゃ。