Category: iOS

Swift:コンテンツブロック

コンテンツブロックのソースコードをDropboxにアップしました。

Source dode: Ads-Blocks Sample code

The sample code is written to evaluate so-called json filters for Safari. MesaViewController is just a test program main, but MesaBlockFileter is developed for MesaClip for iOS. You can manage ads-blocks with the filters on memory. If your users want to customize filters, write the memory filters and read them after their customization, then rewrite them. As the filters are converted from macOS version, there would be my misunderstanding. I hope it is helpful for some app developers. Please refer to iOS 11 WKWebViewで広告などのコンテンツブロックをする by Mr. Shingo Fukuyama as well. The page is very good though it is written in Japanese. In regard to copyright, please refer to the source code.

  • このサンプルプログラムはフィルタの評価をするために作成しました。
  • MesaViewControllerは、フィルタ読み込んだあと、webページを表示し、フィルタが妥当かチェックするための暫定評価プログラムです。
  • MesaBlockFileterは、実際にMesaClip for iOSで使っています。
  • メモリだけで処理した方がプログラム上の操作性がいいので、Swiftに組み込む.jsonファイルの中身は最小限で動作するようになっています。
  • メモリ上のフィルタをカスタマイズしたい場合、これをファイルに書き出し、カスタムフィルタ対応ができるようになっています。
  • フィルタはMesaClip for macOSアプリで使っているものをコンバージョンしました。macOS版は力づくでコンテンツブロックを実装したので、フィルタの互換性がありません。そのため勘違いや間違いがあるかもしれません。

謝意: コンテンツブロックの処理はShingoFukuyamaさんのコードを参考にしています。詳細はiOS 11 WKWebViewで広告などのコンテンツブロックをするを参考にしてください。ShingoFukuyamaさん、ありがとうございます。

Image20180809.002

Image20180809.003

Swift:MesaClip for iOSをリリース

開発着手からとても長い月日が流れてしまいましたが、ようやくMesaClip for iOSをリリースしました。

途中でリリースを諦めようかとも考えたのですが、踏みとどまり、今朝、Appleからアプリの承認がおりました。審査では、スクリーンショットが引っかかり修正が必要でした。また、iTunesファイル共有が必要な理由を説明しました。

スクリーンショットを修正しているとき、ATS(http対応)をNOに変更し、メモ書きのバグが見つかったので修正しました。早く見つかってよかったです。

あとは大きな問題はありませんでした。

iOSについては、またひとつ勉強してしまったというのが今の感想です。

今後は、すでに最新OSに対応済みのMesaWater、MesaExif、MesaAnalyzerのアップデート版をリリース予定です。MesaWaterだけはドキュメント修正がたくさんあるので、先に着手しています。ほかはリリースノート対応で可能です。

あとから思いついたのですが、MesaMeterの出力であるCSVファイルをメールで送れるようにするか、共用ファイルにして、Windows PCのExcelから直接引き出せるようにすれば、手離れがよくなるので、いずれ、検討したいと考えています。

また、次のテーマは、教育か介護がおもしろいのではないかとぼんやり考えています。

スクリーンショット 2018-08-03 00.48.09

Swift:開発メモTwitter

開発中に書いたメモをFacebook PageからTwitterに移行しました。

とは言っても、Twitterは意味不明なアカウント凍結問題が起きており、まだ最終的にこれで落ち着くかどうか不明です。もう一度、Facebook Pageに戻る必要があることを完全に排除できないのは、少し残念なことです。

どちらを利用した方が便利かについては、一長一短があり、決めがたいです。Twitterの問題は、あとから修正できないこと、文字数に限りがあることです。Facebook Pageの方が柔軟性が高いですが、閲覧者とかフォロワーがFBの関係者ではないかという怪しげな点が気がかりです。

ブログがもう少し手軽なら、直接ブログに書いてもいいのですが、スクリーンショットのあげ方が面倒で使い勝手が悪いです。wordpressは改善の余地ありです。

Screenshot 2018-07-22 11.04.23

 

Swift4:UITextView制御(再修正)

placeholderの制御に関して、解決方法がわかりました。

解決方法

  1.  Z.yUserNote.endEditing(true)を付加
    ①このアプリの場合、表の行がクリックされたとき、UITextViewに対して.endEditing(true)を実行します。
    ②textViewDidBeginEditingやtextViewDidEndEditiongでフラグを立て、これを監視する必要はありません。.endEditing(true)を実行すると、UITextViewからキャレットが移動されます。
  2. 設定された値の変更(save)
    当該イベントが終了したときではなく、ほかのイベントがキックされたとき、実施しています(従来通り)。これは、textViewDidEndEditiongで実施しても、結局、ほかのイベントがはじまったときに監視しなければならないからです。
  3. キーボードを閉じる処理
    iPadでは問題がないのですが、iPhone SEだと入力対象エリアがキーボードで隠れてしまい、確認ができないからです。
    ①クリックされた直後にUITextFieldキーボードを閉じる。
    ②textViewDidEndEditingでUITextViewキーボードを閉じる。

@IBAction func ExecIBAGo2Click(){                        //<*Process when a row in UITableView is kicked*>
   self.ExecClickedRow(self.Z.qRow, pCount:0)            // 🔵🔵Click Process🔵🔵
}//🐳🐳🐳🐳🐳🐳🐳🐳🐳🐳🐳🐳🐳🐳🐳🐳🐳🐳🐳🐳🐳🐳🐳🐳🐳🐳🐳🐳
func ExecClickedRow(_ pRow:Int, pCount:Int) {//<*pCount>=0: Click Process pCount<0: Update process after a row removal*>
   Z.ySearchbar.resignFirstResponder()                    //Close a keyboard for UITextField(Search).
   if pRow < 0 || pRow >= Z.rcArray.count || Z.rcArray.count <= 0 {//Available?
       Z.qRow          = -999                           //Out of the Array.
       return                                           //♻️return♻️ Go back.
   } else { }                                           //
   if pCount >= 0 {                                     //1st click or 2nd click and later?
       ExecSetValues2Array(Z.qRow//Save the previous values in tow UITextField and one UITextView.
   } else { }                                           //
   Z.yUserNote.endEditing(true)                         //End editing UITextView.
   Z.qRow              = pRow                           //Set the target row.
   Z.maybeLastRow      = pRow                           //Set the target row just in case.
   ExecSetDateTitleMemo(Z.qRow)                         //Draw Date & Time 📅Group 📚Title.
   Z.sortURL           = Z.rcArray[Z.qRow][Z.fURL] as! String//Set the current URL in case of clicking the screen shot.
   Z.yTableView.selectRow(at: IndexPath(row: Z.qRow, section: 0), animated: true, scrollPosition: .top)//Select the row.
   Z.yTableView.reloadData()                           //🍋🍋reloadData🍋🍋    }//====================================================//
func ExecSetDateTitleMemo(_ pRow:Int) {                //<*Set values to UITextView*>
   Z.yUserNote.text    = “”                            //
   if pRow < 0 || pRow >= Z.rcArray.count { return }   //♻️return♻️ The previous row is out of the array
   else { }                                            //
   Z.yGroup.text       = (Z.rcArray[pRow][Z.fGroup] as! String)//Set Group value.
   Z.yTitle.text       = (Z.rcArray[pRow][Z.fTitle] as! String)//Set Title value.
   let wImage:UIImage  = Com.CallResizeCapturedImage((Z.rcArray[pRow][Z.fScreen] as! UIImage), pImageView:Z.yScreen)//Set Screenshot.
   Z.yScreen.image     = wImage                        //
   let wCheck:NSMutableAttributedString?   = (Z.rcArray[pRow][Z.fNote] as? NSMutableAttributedString) //Check UserNote.
   if wCheck == nil || (wCheck?.string.count)! <= 0  { //Set the placeholder if the caret is out of the UITextView & no data.
       if wkEdited == “” { Z.yUserNote.attributedText = wkPlaceholder }//
       else              {  }                              //
   } else { Z.yUserNote.attributedText = Z.rcArray[pRow][Z.fNote] as! NSMutableAttributedString }//Set UserNote.
}//========================================================//
func textViewDidBeginEditing(_ textView: UITextView) {     //<*Watch the UITextView(Note Area)*>
   if Z.yUserNote.text == wkPlaceholder.string {           //Placeholder string?
      Z.yUserNote.text            = “”                     //No data.
      Z.yUserNote.textColor       = UIColor.black</b>      //Black. (Placeholder: Gray)
   } else {  }                                             //
}//========================================================//
func textViewDidEndEditing(_ textView: UITextView) {       //<*Watch the carsor (caret)*>
  Z.yUserNote.resignFirstResponder()                      //Close keyboard for UITextView(UserNote).
}//========================================================//

Screenshot 2018-06-12 07.34.11

 

Swift4:UITextViewの制御(修正版)

UITextViewをハンドリングする方法についての忘備録です。

要点は次の2点です。

placeholderのハンドリングと変更内容のsaveタイミング

  1. キャレットをUITextViewの外側に出さないとplaceholderの設定が自動的に行われません。外に出さないと、設定したplaceholderが文字列と認識される問題が発生します。解決方法のひとつとして、たとえばUITextFieldに文字列を置き、selectAllで選択すれば、この問題を解消できます。これには、副作用があり、selectAllのあとにキャレットを最初の文字列の前に持っていっても、pasteのポップアップが表示されてしまう問題が起きます。
  2. ない知恵を出し切っていろいろ考えたのですが、結局、次のようにしました。①UITextViewにキャレットがある間はplaceholderを表示しない仕様とする。
    ②キャレットをユーザーがUITextViewの外側に移動した場合、直前のUITextViewの変更内容をsaveし、次からplaceholderが表示される仕様とする。
    ③表のクリックやボタンのクリックなどのアクティビティが起きた場合、直前のUITextViewの変更内容をsaveする。

上記paste popup画面については、pasteできる状態にあるから表示されると推測しています。よって、copy and pasteの機能追加をしてこのpasteを取り消せばいいのではないか、と考えています。以前、macOSでは、copy and pasteを実装したことがあります。その経験からするとそんなにむずかしい話ではありません。少し余裕ができたら検討したいと思います。

 

@IBAction func ExecIBAGo2Click(){//<*Process when a row in UITableView is kicked*>
        self.ExecClickedRow(self.Z.qRow, pCount:0)                 // 🔵🔵Click Process🔵🔵
   }//=============================================================//
    func ExecClickedRow(_ pRow:Int, pCount:Int) {                  //<*pCount>=0: Click Process pCount<0: Update process after a row removal*>
        if pRow < 0 || pRow >= Z.rcArray.count || Z.rcArray.count <= 0 {//Available?
            Z.qRow          = -999                                  //Out of the Array.
            return                                                  //♻️return♻️ Go back.
        } else { }                                                  //
        if pCount >= 0 {                                            //1st click or 2nd click and later?
            ExecSetValues2Array(Z.qRow)                             //Save the previous values in tow UITextField and one UITextView.
        } else { }                                                  //
        Z.qRow              = pRow                                  //Set the target row.
        Z.maybeLastRow      = pRow                                  //Set the target row just in case.
        ExecSetDateTitleMemo(Z.qRow)                                //Draw ⏰Date & Time 📅Group 📚Title.
        Z.sortURL           = Z.rcArray[Z.qRow][Z.fURL] as! String  //Set the current URL in case of clicking the screen shot.
        Z.yTableView.selectRow(at: IndexPath(row: Z.qRow, section: 0), animated: true, scrollPosition: .top)//Select the current row.
       Z.yTableView.reloadData()                                    //🍋🍋reloadData🍋🍋
    }//=============================================================//
    func ExecSetDateTitleMemo(_ pRow:Int) {                         //<*Set values to UITextView*>
        Z.yUserNote.text    = “”                                    //
        if pRow < 0 || pRow >= Z.rcArray.count { return }           //♻️return♻️ The previous row is out of the array.
        else { }                                                    //
        Z.yGroup.text       = (Z.rcArray[pRow][Z.fGroup] as! String)//Set Group value.
        Z.yTitle.text       = (Z.rcArray[pRow][Z.fTitle] as! String)//Set Title value.
        let wImage:UIImage  = Com.CallResizeCapturedImage((Z.rcArray[pRow][Z.fScreen] as! UIImage), pImageView: Z.yScreen)//Set Screenshot.
        Z.yScreen.image     = wImage                                //
        let wCheck:NSMutableAttributedString?   = (Z.rcArray[pRow][Z.fNote] as? NSMutableAttributedString) //Check UserNote.
        if wCheck == nil || (wCheck?.string.count)! <= 0  {         //Set the placeholder if the caret is out of the UITextView and no data.
            if wkEdited == “” { Z.yUserNote.attributedText = wkPlaceholder }//
            else              {  }                                  //
        } else { Z.yUserNote.attributedText = Z.rcArray[pRow][Z.fNote] as! NSMutableAttributedString }//Set UserNote.
    }//=============================================================//
    func textViewDidBeginEditing(_ textView: UITextView) {          //<*Watch the UITextView(Note Area)*>
        if Z.yUserNote.text == wkPlaceholder.string {               //Placeholder string?
            Z.yUserNote.text            = “”                        //No data.
            Z.yUserNote.textColor       = UIColor.black             //Black. (Placeholder: Gray)
            wkEdited                    = “🍎”                      //🍎Begin Editing with no data.
        } else {                                                    //
            wkEdited                    = “🍎”                      //🍎Begin Editing with data.
        }                                                           //
    }//=============================================================//
    func textViewDidEndEditing(_ textView: UITextView) {            //<*Watch the carsor (caret)*>
        if Z.yUserNote.text.count <= 0 || Z.yUserNote.text == wkPlaceholder.string {//No data or placeholder?
            wkEdited                    = “”                        //Clear the Edit sign.
        }else if wkEdited == “🍎” {                                 //Going on editing the UITextView?
            wkEdited                    = “⛔️”                      //⛔️End.
        } else { }                                                  //
    }//=============================================================//

Screenshot 2018-06-10 13.32.22

Swift4:viewを中心に置く

iPhoneとiPadの機種毎のサイズに適合するようにviewを中心に置く方法をあれこれ考えました。

最初、すべてのオブジェクトを配列に定義し、x / yで動かしてみたのですが、よくよく考えたらUIViewで定義しておけば、個々のオブジェクトを動かす必要がありません。

しかし、iPadの場合、画面サイズが大きいのでローテーションに対応すべきで、そうなるとLandscapeだけに対応すればいいという安直回答は不適です。そう思いながら実装してみると、ローテーションするたびにシフト幅が加算されてしまうバグが出てしまい、結局、オブジェクトの全体サイズを知る必要がありました。

ない知恵を絞った結果、見えないUIButtonを下側に設置し、これから計算すれば面倒な計算をしなくていいとわかりました。

Screenshot 2018-06-03 10.35.01

Screenshot 2018-06-03 10.51.27

Screenshot 2018-06-03 11.03.05

Swift:modal delegate vs. storyboard ID

iOSの画面遷移の仕方がよくわかっていないという根本的な問題は一旦横に置いて、結局、storyboard IDで遷移した方が簡単です。

それに、独立性が高いので、書き方・考え方だけでなく、呼び出しも簡単です。結局、全部、storyboard IDで遷移する方法に書き換えました。もうひとついい点は、entry pointを設定しなくても遷移できるので、UIを一箇所にまとめて管理できます。

Screenshot 2018-05-30 21.27.23