uka.apple のすべての投稿

独創アプリ開発日記 72日目 固定画面

今日はクリスマスイブですね。ちまちまアプリ作ってる場合ではありません。今日くらいはぱーっと遊びましょう。

はい、というわけで、今回はアプリのトップ画面を作っていました。

スクロールしない画面です。

コードは以下のような感じです。テキスト等を画面のどの位置に配置するか、画面のサイズに対する比率を指定して配置するようにしました。

やろうと思えば絶対座標で位置を指定出来るので、まるでネイティブアプリを作っているような錯覚に陥ります。

<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no">
        <style type="text/css">
        .vintage {
            background: #EEE                         url(data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAIAAAAmkwkpAAAAHklEQVQImWNkYGBgYGD4//8/A5wF5SBYyAr+//8PAPOCFO0Q2zq7AAAAAElFTkSuQmCC) repeat;
            text-shadow: 5px -5px black, 4px -4px white;
            font-weight: bold;
            -webkit-text-fill-color: transparent;
            -webkit-background-clip: text
        }
        .title_label {
            font-size: 48px;
            text-align: center;
            width: 400px;
            line-height:150px;
        }
        .txtbase {
            position: absolute;
            -moz-user-select: none; /* Firefox */
            -ms-user-select: none; /* Internet Explorer */
            -khtml-user-select: none; /* KHTML browsers (e.g. Konqueror) */
            -webkit-user-select: none; /* Chrome, Safari, and Opera */
            -webkit-touch-callout: none; /* Disable Android and iOS callouts*/
        }
        .btn {
            font-size: 36px;
            text-align: center;
            width: 400px;
            line-height:100px;
        }
        </style>
        <title>TextManga</title>
    </head>
    <body>
        <span id="title" class="title_label txtbase vintage">てきすとまんが</span>
        <a id="mine_btn" class="btn txtbase vintage" href="<?php echo url(''); ?>">じぶんのまんが</a>
        <a id="everyone_btn" class="btn txtbase vintage" href="<?php echo url(''); ?>">みんなのまんが</a>
        <a id="other_btn" class="btn txtbase vintage" href="<?php echo url(''); ?>">他の人のまんが</a>
        <a id="help_btn" class="btn txtbase vintage" href="https://minnano.app/support/2015/02/01/usage_of_textmanga_1/">つかいかた</a>
        <script type="text/javascript" src="<?php echo env('ASSETS_PATH').'/js/jquery-2.1.0.min.js'; ?                >">
        </script>
        <script type="text/javascript">
        // スクロールを無効にする
        $(window).on('touchmove.noScroll', function(e) {
            e.preventDefault();
        });
        function adjust() {
            var w = $(window).width();
            var h = $(window).height();
            if (w < h) {
                // タイトル
                setPos("#title", 0.5, 0.2, w, h);
                // 各ボタン
                var y_base = 0.5;
                var pitch = 0.12;
                setPos("#mine_btn", 0.5, y_base + pitch * 0, w, h);
                setPos("#everyone_btn", 0.5, y_base + pitch * 1, w, h);
                setPos("#other_btn", 0.5, y_base + pitch * 2, w, h);
                setPos("#help_btn", 0.5, y_base + pitch * 3, w, h);
            } else {
                // タイトル
                setPos("#title", 0.5, 0.05, w, h);
                // 各ボタン
                var y_base = 0.4;
                var pitch = 0.2;
                setPos("#mine_btn", 0.5, y_base + pitch * 0, w, h);
                setPos("#everyone_btn", 0.5, y_base + pitch * 1, w, h);
                setPos("#other_btn", 0.5, y_base + pitch * 2, w, h);
                setPos("#help_btn", 0.5, y_base + pitch * 3, w, h);
            }
        }
        function setPos(selector, rate_x, rate_y, w, h) {
            var ih = parseInt($(selector).css("height"));
            var iw = parseInt($(selector).css("width"));
            $(selector).css("left", (w - iw) * rate_x + "px");
            $(selector).css("top", (h - ih) * rate_y + "px");
        }
        $(document).ready(function () {
            adjust();
        });
        $(window).resize(function () {
            adjust();
        });
        </script>
    </body>
</html>

なお、リンク先はまだ全然作っていません。

    :/ ̄| :  :  ./ /  #  ;,;  ヽ 
  :. | ::|    /⌒  ;;#  ,;.;::⌒ : ::::\ : 
    | ::|:  / -==、   '  ( ●) ..:::::| 
  ,―    \   | ::::::⌒(__人__)⌒  :::::.::::| :  
 | ___)  ::|: ! #;;:..  l/ニニ|    .::::::/ 
 | ___)  ::|  ヽ.;;;//;;.;`ー‐'ォ  ..;;#:::/ 
 | ___)  ::|   .>;;;;::..    ..;,.;-\ 
 ヽ__)_/ :  /            \   
 

独創アプリ開発日記 71日目 アプリ専用画面

今日も引き続きテキスト漫画サイトのアプリ化作業です。

今回は、アプリから同サイトを参照した場合、アプリに不要な機能を削除していました。

・アプリは端末IDを元に自動でログインするため「ログイン」や「アカウント作成」を削除

・「手動コピー」のリンクは、自動コピーができないIEなどのブラウザ用ですが、アプリなら必ず自動コピーが成功するので削除

・ページ下部の「uka.appleサイトへのリンク」を削除

とてもシンプルな画面になりました。

これでアプリ申請したいところですが、まだWebっぽいページなのでもうちょっと改良します。

以下画面の作成を予定しています。

・タイトル画面

以下のメニューを選べます。

[自分の漫画]—自分が作った漫画をリスト表示します。
[みんなの漫画]—みんなで編集可能な漫画をリスト表示します。
[他の人の漫画]—自分以外の人が作った漫画をリスト表示します。
[使い方]—使い方画面に遷移します。

・使い方画面

[コピーの仕方]—テキスト漫画の貼り付け手順を説明します。
[編集の仕方]—テキスト漫画の作り方を説明します。
[Q&A]—このページの内容を表示します。
[あなたのID]—IDを表示します。また、IDの入力フィールドを用意して他の端末からの移行を可能とします。

上記2つの”スクロールしない”画面を作成すれば、アプリっぽくなると思います。Webサイトをそのままアプリ化するとRejectされるという噂ですが、上記画面を作ればRejectされないはず!!

            i⌒i スッ
             | 〈 
     Apple様  / .フ
      ( ^+^)/  |  
    /      /  ノ
   / /\   /   |
 _| ̄ ̄ \ /.  ノ__
 \ ̄ ̄ ̄ ̄ ̄ ̄( ^ν^)
  ||\            \
  ||\|| ̄ ̄ ̄ ̄ ̄ ̄ ̄|| ̄
  ||  || ̄ ̄ ̄ ̄ ̄ ̄ ̄||
     .||              || 

独創アプリ開発日記 70日目 alert, confirm, prompt

今日も引き続きテキスト漫画のガワネイティブアプリ化です。

ガワネイティブアプリでWebを表示するのに多用されるWKWebViewですが、JavaScriptのalert,confirm,promptに関してはデフォルトで何も表示してくれません。

alert,confirm,promptのダイアログを表示させるには UIAlertAction で適切に処理してあげる必要があります。

 

と言っても定型でほぼ処理コードは決まっているため、以下のように書いてあげれば大丈夫です。Swift4のコードです。


import UIKit
import WebKit

class ViewController: UIViewController, WKUIDelegate {

    private var _webView : WKWebView!
    〜略〜
    override func viewDidLoad() {
        super.viewDidLoad()
        〜略〜
        _webView.uiDelegate = self
        〜略〜
    }

    func webView(_ _webView: WKWebView, runJavaScriptAlertPanelWithMessage alert: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {
        
        let alertController = UIAlertController(title: "", message: alert, preferredStyle: .alert)
        let otherAction = UIAlertAction(title: "OK", style: .default) {
            action in completionHandler()
        }
        alertController.addAction(otherAction)
        self.present(alertController, animated: true, completion: nil)
    }
    
    func webView(_ _webView: WKWebView, runJavaScriptConfirmPanelWithMessage confirm: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) {
        
        let alertController = UIAlertController(title: "", message: confirm, preferredStyle: .alert)
        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) {
            action in completionHandler(false)
        }
        let okAction = UIAlertAction(title: "OK", style: .default) {
            action in completionHandler(true)
        }
        alertController.addAction(cancelAction)
        alertController.addAction(okAction)
        self.present(alertController, animated: true, completion: nil)
    }
    
    func webView(_ _webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void) {
        
        let alertController = UIAlertController(title: "", message: prompt, preferredStyle: .alert)
        
        alertController.addTextField { (textField) in
            textField.text = defaultText
        }
        alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { (action) in
            completionHandler(nil)
        }))
        alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) in
            if let textField = alertController.textFields?.first?.text {
                completionHandler(textField)
            } else {
                completionHandler(defaultText)
            }
        }))
        self.present(alertController, animated: true, completion: nil)
    }    
    〜略〜
}

 
 
 
    _,,..,,,,_
   / ,’ 3  `ヽーっ
   l   ⊃ ⌒_つ
    `’ー—‐””'” 
 

独創アプリ開発日記 69日目 キーチェーンアクセス

引き続き、テキスト漫画サイトのガワネイティブアプリの作成を行っていました。

今日は、昨日諦めたキーチェーンアクセス処理を頑張って実装し、アプリ再インストールの場合でもユーザ識別用のIDを復元することが出来るようになりました!!

処理の流れとしては

1. UserDefaultsに保存したIDを確認

2. 1でIDが取得できればそれを使う

3. 1でIDが取得できなければキーチェーンからIDを取得

4. 3でIDを取得できればそれを使う。UserDefaultsにIDを保存。

5. 3でIDが首都できなければIDFVを取得して、それをIDとしてUserDefaultsとキーチェーンに保存

としました。

UserDefaultsはアプリ再インストールで消えてしまう情報、キーチェーンはアプリ再インストールでも消えない情報です。

上記の流れにしておけば、もしアプリが再インストールされた場合でも 1 > 3 > 4 の流れでIDが復元できますし、アプリが再インストールされていない場合はUserDefaultsから素早くIDを取得出来ます。

キーチェーンアクセスを行うライブラリは色々ありますが、1つの文字列を同一アプリ内で保存して利用するだけなので、このURLのベージ下部のswift4用コードを参考に最小限での実装としました。

iOSで使う場合は、いくつかMacOS用のコードでエラーが出るので気を付ける必要がありますが、数行だけですので簡単に直せるかと思います。

上記URLのコードがビダァァァン!!!!と私の使用用途にハマりました。

 
 
 
    _,,..,,,,_
   / ,’ 3  `ヽーっ
   l   ⊃ ⌒_つ
    `’ー—‐””'” 
 

独創アプリ開発日記 68日目 アプリへのidfv保存

引き続き、テキスト漫画サイトのガワネイティブアプリの作成を行っています。

今日は、昨日取得したユーザを識別するID(idfv)をアプリで保存するようにしました。

基本的にidfvは変わらないものなのですが、過去、iOSでアプリをアップデートするとidfvが変わってしまうというバグがあったらしいです。そのため、念のため1度取得したidfvはアプリにデータとして保存し、2回目以降は保存したデータからidfvを取得しようと思います。

こうすることで、もし再度バグなどが発生してidfvが変わってしまっても大丈夫になります。

コードは以下のようになりました。

struct Constants {
    enum User : String {
        case idfv
    }
}
〜 略 〜
// デバイスの識別
let idfv : String!
if UserDefaults.standard.object(forKey: Constants.User.idfv.rawValue) != nil {
    // UserDefaultsにある場合、それを使用
    idfv = UserDefaults.standard.string(forKey: Constants.User.idfv.rawValue)
} else {
    // UserDefaultsにない場合、IDFVを取得
    idfv = UIDevice.current.identifierForVendor!.uuidString
    // idfvは36文字だが、念のためハイフンをのぞいて32文字以上あれば正常に取得できたと判断して保存
    if (32 <= idfv.count) {
        // 保存
        UserDefaults.standard.set(idfv, forKey: Constants.User.idfv.rawValue)
        // 削除する場合は以下
        //UserDefaults.standard.removeObject(forKey: Constants.User.idfv.rawValue)
    }
}

尚、アプリを再インストールした場合(正確には同一ベンダーのアプリが全てアンインストールされた場合)もidfvが変わってしまいますが、その場合はUserDefaultsも消えてしまうのでどのみちidfvは保持出来ません。

UserDefaultsとは別に、Keychain Servicesというものがあり、こちらを使えばアプリをアンインストールしても値を維持出来そうです。一瞬、これを利用しようかと思いましたが、

めんどうそうなので諦めました。

                  ,!  \  
           ,!\          !    \       
         i  \         l      \,,..__  
          ,i′  ,\___,,--―l       \::゙'冖ーi、、  
        i     :;\::::::::::..l              `'‐、、  
       /__,..;:r---―-、,..__.     ,;'il:;}          .;:::`L__  
   ,.:f''""゙゙゙´          、 ̄ヽ,//           ...::::::l;;;:;;::::  
  _/       ......  、   \//、            ::::::::リ;;:::::::::....  
//       ......:;::::::::::::. ヽ、\ ゙ヽ  ヘ    ●      ....:::::::::i';;;;::::::::::::  
;;/    ::::::::::::;;;;;ノ ̄\:: 〉 〉゙'、 `ヽ_ノ       ......:::::::.;;;:ノ:;;;:::::::::::::  
/    ..::::、__;;ノ;;;`ヽ_/: / /⌒)メ、_ノ/         .....:::::;;;/;;;:::::;;:::::::::  
     ..:::イ;;.ヽ::;;;;;;;;;(__ノ /'"..:::::::::::::/  ...............:::::::::::;;;,;ノ;;::::::::::::::::  
     :::::::l;;;;;;;;;\;;;;;;;,.(__ノ;.;:.\:::::::::/::::::::::::::::::::::::::::;;;;;/;:::::::::::::::::  
    ::::::::,!::;;;;;;;;;;:.`゙'-、、  ::: \_/::::::::::;;;___,.;-―''"::::::::::::::::::::::::  
   ..::::::::::,!;;;;;:;;;;;:::;;;;;:::;;;;;;`゙ ̄'''冖''―--―'";;;;;;;;;::::::::::::::::::::::::: 
 

独創アプリ開発日記 67日目 アプリ判定

今日はアプリからテキスト漫画のサイトを見た場合のみ、ガワネイティブアプリ専用のデザインに変わるようにしました。

仕組み的にはアプリからアクセスする場合にiOSのidfvという識別IDをURLに付与して、それを元にcssを切り替えています。

以下のようなコードで取得します。

let idfv : String = UIDevice.current.identifierForVendor!.uuidString

ご覧の通り、スマホでアクセスしてもsafariからだと従来のデザインで表示されます。

 

よし、これでアプリ審査通る・・・か?Webサイトをそのままアプリ化するとreject食らうっていう話だけど、これなら良い筈だ・・・

  
                |
     Apple様       ||
      ( ^+^)    .|||
    /     \    |||| Reject!!
   / /\   / ̄\ ||||      .’  , ..
 _| ̄ ̄ \ /  ヽ \从// ・;`.∴ ’     
 \ ̄ ̄ ̄ ̄ ̄ ̄ \__) < ,:;・,‘  
  ||\            \     ’ .’ , ..
  ||\|| ̄ ̄ ̄ ̄ ̄ ̄ ̄|| ̄
  ||  || ̄ ̄ ̄ ̄ ̄ ̄ ̄||
     .||              || 

独創アプリ開発日記 66日目 アプリ判定

今日は、アプリかどうかによってcssを切り替える作業を行いました。

phpでユーザーエージェント $_SERVER[‘HTTP_USER_AGENT’] にiPhone, iPad, iPodの文字が含まれているかどうかでひとまずは判定しています。

iPhoneやiPad,iPodでテキスト漫画のサイトにアクセスすると、cssが切り替わるようになりました。

Android?

知らない子ですねぇ・・・

    ∧ ∧___ 
   /(*゚ー゚) /\
 /| ̄∪∪ |\/
   |アンドロイド|/
     ̄ ̄ ̄ ̄

独創アプリ開発日記 65日目 css下準備

今日は、ガワネイティブアプリを作成するための準備としてデザイン部分をcssファイルにまとめていました。

ガワネイティブアプリはPC用のサイトと同じページを利用しますが、デザインだけはアプリ専用にします。

デザイン部分をcssファイルにまとめることで、PC用とガワアプリ用のCSSを手軽に切り替えられるようにします。

     ∧∧
    (,,゚ー゚)
  ~(___ノ

↑ちびしぃです。特に意味はありません。

独創アプリ開発日記 64日目 続改良

今日もテキスト漫画のサイトに手を入れていました。

・今日やったことその1

複数の半角スペースをWebで表示しようとすると、1つの半角スペースにまとまってしまうのはWebの仕様ですが、この仕様はテキスト漫画を作成する場合に混乱を招きます。

そこで、テキスト漫画を表示する際は半角スペースを特殊文字にして、1つにまとまらないようにしました。

・今日やったことその2

テキスト漫画のサイトとWordpressのサイトに同じテキスト漫画を表示した場合、微妙に表示が異なっていることに気づきました。調べたところ、lang指定が原因でした。

<html lang=”ja”>

これで表示変わっちゃうんだね・・・この対策も行いました。

・今日やったことその3

フォント指定のタグを、標準でコピーするようにしました。

↓こんなのです。

<p lang=”” style=”font-family:’MS PGothic’,’MS Pゴシック’,sans-serif;font-size: 12pt; line-height:normal;”>
・・・テキスト漫画部分・・・
</p>

これで「コピーする!」のリンクからコピペするだけで手軽に使えます。

もう完璧だと思います。

 ∧_∧     
 ( ・ω・)=つ≡つ 
 (っ ≡つ=つ=つ 
 /   ) バババ
 ( / ̄∪ 
ボコボコにしてやんよ。

独創アプリ開発日記 63日目 それぞれの環境

昨日からAAズレの対策を色々検討していましたが、よくよく考えたら問題ありませんでした。

スマホ使う人:スマホで作ってスマホで使う → ズレない
PC使う人:PCで作ってPCで使う → ズレない
私のような開発者:PCやスマホで作ってスマホやPCやいろんな環境で確認する → 色々ズレる

うん、多分PCやスマホ混在(WinやMac混在)で使う人少ないよね。

既に解決してた。

      ∧_∧ 
     _(  ´Д`) 
    /      ) 
∩  / ,イ 、  ノ/   
| | / / |   ( 〈 ∵. 
| | | |  ヽ  ー=- ̄ ̄=_、 
| | | |   `iー__=―_ ;,
| |ニ(!、)   =_二__ ̄_=;,
∪     /  /
     /  / 
    / _/  
    ヽ、_ヽ 
 

いや、でも自分以外の不特定多数に見てもらいたいから、いろんな環境で見られるよなぁ。全然解決してないわ。でも使う人がきっとなんとか解決方法を編み出してくれるはずだ。多分。

  ワケ
  ∧_∧
 ( ・∀・)
⊂ ⊂  )
 < < <
 (_(_)