TextFieldに英数記号のキーボードのみを表示する方法【Swift】

■はじめに

ここでは、以下の要望への対応方法を記載します。

  • TextFieldにフォーカスを当てた時のキーボードを、英数記号のみにして欲しい
  • ユーザーの設定関係なく、英数記号のキーボードにして欲しい。
  • キーボードの切り替えを許さないで欲しい
  • パスワード入力以外のTextFieldに使いたい(●は出さないで欲しい)

iOSの事を知らない人はこんな無茶苦茶な要望してくる人もいるかもしれません。

(実際にされた人を知っていますw)

上記は、iOSのデフォルト機能だけでは実現することができません。

「●」は出さないで欲しい。が一番の原因ですね。。。

■手順

最終的にこんな感じのキーボードが表示されます。

(パスワード用のキーボードだとスクショに映らないので、写真で取りました。。。見辛くてすみません。。。)

まずは、結論から。

CustomTextField.swiftを作成して以下をコピペしてください。

import UIKit
class CustomTextField: UITextField, UITextFieldDelegate {
// パスワード入力フィールド(画面上には表示しない)
var passText = UITextField()
/// 初期化処理
/// - Parameter frame: frame description
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
/// 初期化処理
/// - Parameter aDecoder: aDecoder description
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
/// 初期化処理で呼び出す共通処理
func commonInit() {
// 非表示のテキストフィールドをパスワード入力フィールドに設定
passText.isSecureTextEntry = true
// キーボードの上のiCloudから入力する「パスワード」の部分の非表示
if #available(iOS 12.0, *) {
passText.textContentType = .oneTimeCode
}
// TextFieldのデリゲートでフォーカス時、値入力時の制御を行う
self.delegate = self
passText.delegate = self
// パスワード入力テキストフィールドを非表示状態でViewに追加
passText.frame = CGRect(x: 0, y: 0, width: 0, height: 0)
self.addSubview(passText)
}
/// フォーカス時処理
/// - Parameter textField: textField description
func textFieldDidBeginEditing(_ textField: UITextField) {
// フォーカスが当たったあと、一瞬遅延させる(即時にフォーカス切り替えが動かないので)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
// フォーカス制御はメインスレッドで
DispatchQueue.main.async {
// パスワード入力TextFIeldにフォーカスを当てる
self.passText.becomeFirstResponder()
}
}
}
/// 値入力(削除)時処理
/// - Parameters:
///   - textField: textField description
///   - range: range description
///   - string: string description
/// - Returns: description
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
// 値をTextFieldに反映
self.text = textField.text
// 一瞬遅延
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
// メインスレッドで処理
DispatchQueue.main.async {
// パスワード入力TextFieldの値を表示されているTextFieldに設定
self.text = self.passText.text
}
}
return true
}
}

使い方は以下のような感じで、使ってください。(普通のUITextFieldと同じです)

let text = CustomTextField()
text.frame = CGRect(x: 50, y: 160, width: 100, height: 30)
text.layer.borderColor = UIColor.blue.cgColor
text.layer.borderWidth = 1
self.view.addSubview(text)

この方法のポイントとしては、「passText」です。

これを画面に見えないように精製して、TextFieldにフォーカスされたタイミングで「passText」にフォーカスさせています。

で、「passText」に値が入力された際にTextFieldに反映させています。

結果、TextFieldに値が入力されているようにみえます。

他に方法があれば、是非ご連絡ください。

axiosを使用してRestAPIを呼び出す方法【React】

■はじめに

実際にシステムを作る際には、RestAPIの実行は必須と言って良いほど、使用します。

ここでは、「axios」を使用したRestAPIの実行方法を記載します。

■手順

axiosをインストールするために、以下のコマンドを実行する

npm i axios

App.jsのソースコードの全量は以下。

import React from 'react';
import axios from 'axios';
class App extends React.Component{
render()
{
function btnGetClick() {
const data = { param : 'aa', paramtwo : 'aa' };
axios.get('https://hogehoge.com/hoge', {params: data})
.then((res) => {
console.log(res);
})
.catch(console.error);
}
function btnPostClick() {
const data = { param : 'aa', paramtwo : 'aa' };
axios.post('https://hogehoge.com/hoge', data)
.then((res) => {
console.log(res);
})
.catch(console.error);
}
return (
<div>
<h1>Hello World</h1>
<button onClick={btnGetClick}>GET通信</button>
<button onClick={btnPostClick}>POST通信</button>
</div>
);
}
}
export default App;

以下でaxiosのインポートをしています。

import axios from 'axios';

「btnGetClick」「btnPostClick」でそれぞれ、リクエストパラメータありのAPIの実行処理を書いています。

違いは、呼び出している関数が「get」「post」なのと、第二引数(パラメータ)の指定方法です。

「get」は以下のように、「{params: [パラメータ]}」というような書き方をする必要があります。

const data = { param : 'aa', paramtwo : 'aa' };
axios.get('https://hogehoge.com/hoge', {params: data})
.then((res) => {
console.log(res);
})
.catch(console.error);

ローカルストレージに値を保存する方法【React】

■はじめに

データの保持方法は色々とありますが、ここでは「ローカルストレージ」の使用方法を記載します。

■手順

ボタン押下時に、データの取得、設定、削除を行うサンプルの全量(App.js)

※確認は、「F12」を押して、コンソールを開いて確認してください。

import React from 'react';
class App extends React.Component{
render()
{
function btnClick() {
console.log("~~~~~データ設定前の値(null)~~~~~");
console.log(localStorage.getItem('aaaa'));
console.log("~~~~~データ設定~~~~~");
localStorage.setItem('aaaa', 'サンプル');
console.log("~~~~~データ設定後の値~~~~~");
console.log(localStorage.getItem('aaaa'));
console.log("~~~~~データ削除~~~~~");
localStorage.removeItem('aaaa');
console.log("~~~~~データ削除後の値(null)~~~~~");
console.log(localStorage.getItem('aaaa'));
}
return (
<div>
<button onClick={btnClick}>ボタン</button>
</div>
);
}
}
export default App;

データは以下の方法で設定できます。

localStorage.setItem('key', 'value');

データは以下の方法で取得できます。

localStorage.getItem('key');

データは以下の方法で削除できます。

localStorage.removeItem('aaaa');

SQLite3でINSERT時にPrepared Statementを複数使用する方法【Swift】

■はじめに

以下のサイトを参考に、SQLite3は使用できるようにしておいてください。(基本的なことは飛ばして説明します。)

www.reigle.info

■手順

まずは、ソースコードの全量は以下

func insert() {
var stmt: OpaquePointer?
let queryString = "INSERT INTO sampleTable (name, age) VALUES (?, ?)"
// クエリを準備する
if sqlite3_prepare(db, queryString, -1, &stmt, nil) != SQLITE_OK{
let errmsg = String(cString: sqlite3_errmsg(db)!)
print("error preparing insert: \(errmsg)")
return
}
// prepared statementを複数定義するために非定常の定義
let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self)
// 1つめの?の定義
if sqlite3_bind_text(stmt, 1, "ひとつめ", -1, SQLITE_TRANSIENT) != SQLITE_OK{
let errmsg = String(cString: sqlite3_errmsg(self.db)!)
print("failure binding name: \(errmsg)")
return
}
// 2つめの?の定義
if sqlite3_bind_text(stmt, 2, "ふたつめ", -1, SQLITE_TRANSIENT) != SQLITE_OK{
let errmsg = String(cString: sqlite3_errmsg(self.db)!)
print("failure binding name: \(errmsg)")
return
}
// クエリを実行する
if sqlite3_step(stmt) != SQLITE_DONE {
let errmsg = String(cString: sqlite3_errmsg(db)!)
print("failure inserting hero: \(errmsg)")
return
}
print("データが登録されました")
}

重要なのは、以下の定義です。

これは、複数のPrepared Statementを使用するための「おまじない」だと思ってください。

let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self)

上記のおまじないの定数を「sqlite3_bind_text」の第五引数に設定することで、複数のPrepared Statementに値をそれぞれ設定できます。

キーチェーンの共通クラスの作成【Swift】

■はじめに

ライブラリは使用しないで、キーチェーンを使用する共通処理を記載します。

なるべく保守性の高いクラスにしようとして作成しました。

(今後、修正するかも。。。)

あくまで、参考までに。

■手順

「KeyChain.swift」クラスを作成する。

以下を丸々コピペで「KeyChain.swift」に貼り付けてください。

※KeyChainの一意のKeyはBundleIDにしています。

import Foundation
class KeyChain {
// KeyChainクラスのインスタンス取得
public static let shared = KeyChain()
// KeyChainの一意のKey情報(BundleID)
fileprivate let privateKey: String = Bundle.main.bundleIdentifier ?? ""
// 保存
func setKeyChain(_ value: String?, key: String) {
let data = value?.data(using: .utf8)
guard let _data = data else {
deleteKeyChain(key: key)
return
}
let dic: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
kSecAttrGeneric as String: key,
kSecAttrAccount as String: privateKey,
kSecValueData as String: _data]
var itemAddStatus: OSStatus?
// 保存データが存在するかの確認
let matchingStatus = SecItemCopyMatching(dic as CFDictionary, nil)
if matchingStatus == errSecItemNotFound {
// 保存
itemAddStatus = SecItemAdd(dic as CFDictionary, nil)
} else if matchingStatus == errSecSuccess {
// 更新
itemAddStatus = SecItemUpdate(dic as CFDictionary, [kSecValueData as String: _data] as CFDictionary)
} else {
print("保存失敗1")
}
// 保存・更新ステータス確認
if itemAddStatus == errSecSuccess {
print("正常終了")
} else {
print("保存失敗")
}
}
// 取得
func getKeyChain(key: String) -> String? {
let dic: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
kSecAttrGeneric as String: key,
kSecAttrAccount as String: privateKey,
kSecReturnData as String: kCFBooleanTrue]
var data: AnyObject?
let matchingStatus = withUnsafeMutablePointer(to: &data){
SecItemCopyMatching(dic as CFDictionary, UnsafeMutablePointer($0))
}
if matchingStatus == errSecSuccess {
print("取得成功")
if let getData = data as? Data,
let getStr = String(data: getData, encoding: .utf8) {
return getStr
}
print("取得失敗: Dataが不正")
return nil
} else {
print("取得失敗")
return nil
}
}
// 削除
func deleteKeyChain(key: String) {
// 削除するqueryを設定
let dic: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
kSecAttrGeneric as String: key,
kSecAttrAccount as String: privateKey]
if SecItemDelete(dic as CFDictionary) == errSecSuccess {
print("削除成功")
} else {
print("削除失敗")
}
}
}

このクラスの使用方法は以下です。

(なるべく、UserDefaultsに近い書き方ができるようにしています。)

// 値の取得
print(KeyChain.shared.getKeyChain(key: "key"))
// 値の設定
KeyChain.shared.setKeyChain("値", key: "key")
// 値の削除(以下のどちらでも可)
KeyChain.shared.deleteKeyChain(key: "kkkk")
KeyChain.shared.setKeyChain(nil, key: "key")

CORSエラーの回避方法【Node.js】

■はじめに

CORSとは、「Cross Origin Resource Sharing」の略です。

このエラーは、異なるドメインからアクセスされた際に発生します。

つまり、localhostで実行しているWEBアプリから、サーバーにデプロイされているRestAPIを実行する時などで発生します。

例)「http://aaa.com/」のWebから「http://bbb.com/」のNode.jsを実行する。

ここでは、CORSエラーが発生しないようにする手順を記載します。(ライブラリは使用しません。)

■手順

express-generatorでプロジェクトが作成されている前提で記載します。

「app.js」の「app.use」が並んでいるところに、以下を追加します。

app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
res.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS, PUT, DELETE");
next();
});

たったこれだけです。

あとは、サーバーを再起動して確認してください。

一応、以下に「app.js」の全量を載せておきます。(ほぼ自動生成ですが。)

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
res.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS, PUT, DELETE");
next();
});
app.use('/', indexRouter);
app.use('/users', usersRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;

Dockerの環境構築(mac)【Docker】

■はじめに

本記事は、macでの環境構築です。

dockerに関する詳しい説明などは記載しておりません。

■手順

こちらの公式サイトより、Docker Hubのアカウントを作成します。

こちらの公式サイトで「Get Docker」を押下して、Dockerのdmgファイルをダウンロードします。

ダウンロードしたら、「dmg」を実行して、Docker Desktopアプリを取得します。

Dockerアプリを起動して、画面の指示通りに進めていくと、チュートリアルコンテナが起動されます。

これで、環境構築は終了です。

ターミナルで、以下を実行することで、Dockerがインストールされている事が確認できます。

docker --version

スプラッシュのViewを取得する方法【Swift】

■はじめに

以下のようにスプラッシュのViewを取得したい場合があります。(たまに)

  • スプラッシュ表示後も一定時間スプラッシュを表示したい
  • 特定の画面でローディングダイアログなどの代わりにスプラッシュを表示したい(裏で画面遷移するなど)

その場合に、スプラッシュと同じような構成でViewを作成してもいいですが、二度手間になります。

ここでは、簡単にスプラッシュからViewを取得して使用する方法を記載します。

■手順

以下のように記載することで、スプラッシュのViewを取得できます。

let splashStoryboardName = Bundle.main.object(forInfoDictionaryKey: "UILaunchStoryboardName") as? String ?? ""
let storyboard = UIStoryboard(name: splashStoryboardName, bundle: nil)
let viewController = storyboard.instantiateInitialViewController()
let splashView = viewController?.view ?? UIView()

まずは以下でSplashで使用しているStoryboardの名前を取得します。

基本は「LaunchScreen」になっていると思いますが、変更されても問題ないようにInfo.plistから取得しています。

Info.plistの「Launch screen interface file base name」がSplashのストーリーボード名です。

let splashStoryboardName = Bundle.main.object(forInfoDictionaryKey: "UILaunchStoryboardName") as? String ?? ""

上記で取得したStoryboard名から、Storyboardのインスタンスを取得します。

let storyboard = UIStoryboard(name: splashStoryboardName, bundle: nil)

取得したStoryboardの「Is Initial View Controller」にチェックされているViewControllerを取得します。

let viewController = storyboard.instantiateInitialViewController()

該当のViewControllerの一番親のViewを取得します。

※このViewがスプラッシュのViewになります。

let splashView = viewController?.view ?? UIView()

■おまけ

以下のようにすることで、どのような画面でも、一番上にスプラッシュのViewを表示することが可能です。

let splashStoryboardName = Bundle.main.object(forInfoDictionaryKey: "UILaunchStoryboardName") as? String ?? ""
let storyboard = UIStoryboard(name: splashStoryboardName, bundle: nil)
let viewController = storyboard.instantiateInitialViewController()
let splashView = viewController?.view ?? UIView()
let window = UIApplication.shared.keyWindow!
window.addSubview(splashView);

任意のタイミングで、以下のようにViewを破棄することで、スプラッシュ表示前の画面に戻せます。

splashView.removeFromSuperview()

クリップボードに文字列をコピーする方法

■手順

以下の関数の第一引数にコピーしたい文字列を渡すことでクリップボードへコピーできます。

function copyText(target){
// コピー用の仮テキストエリア作成
const txtarea = document.createElement("textarea");
txtarea.textContent = target;
// bodyに仮テキストエリアを設定
const body = document.getElementsByTagName("body")[0];
body.appendChild(txtarea);
// コピー
txtarea.select();
document.execCommand('copy');
// コピー用の仮テキストエリア削除
body.removeChild(txtarea);
}

Dockerコマンド集【Docker】

コマンド 説明
docker ps -a コンテナを全て表示
docker ps 起動中のコンテナを全て表示
docker rm [コンテナ名] コンテナの削除
docker build -t [任意の名前] . イメージの作成(ビルド)
docker run -it –name [任意のコンテナ名] -p 3000:3000 -v $PWD:/usr/src/app [イメージビルドで使用した名前] コンテナ起動
docker image ls イメージ一覧確認
docker image rm [イメージID] イメージの削除
docker logs [コンテナ名] コンテナのログ確認
docker start [コンテナ名] コンテナ起動
docker stop [コンテナ名] コンテナ停止