tag:blogger.com,1999:blog-34039356795253130172024-03-13T12:27:23.906+09:00baroqueworksdevの日記baroqueworkdevhttp://www.blogger.com/profile/05590925357596481434noreply@blogger.comBlogger125125tag:blogger.com,1999:blog-3403935679525313017.post-82419206246928990562021-10-03T13:17:00.000+09:002021-10-03T13:17:17.172+09:00UITabBarAppearanceを使って、TabBarの配色をセットするiOS13以降のTabBarのカスタマイズです。<br/>
今回の記事では、次のようにTabBarをカスタマイズしました。<br/>
<br/>
1. UITabBarを継承した、カスタムViewを作成する<br/>
2. TabBarItemの配色をUITabBarItemAppearanceで作成する<br/>
3. UITabBarAppearanceを生成する<br/>
4. UITabBarにセットする<br/>
5. UITabBarViewControllerにて、カスタムViewを使う<br/>
<br/>
<br/>
UITabBarを継承した、カスタムViewは次になります。<br/>
<br/>
<pre class="prettyprint lang=swift linenums" style="overflow: auto; word-wrap: normal;">
final class MyUITabBar: UITabBar {
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupView()
}
private enum Const{
static let backgroundColor: UIColor = .systemGroupedBackground
static let tintColor: UIColor = .gray
static let selectedColor: UIColor = .black
}
private func setupView() {
let tabBarItemAppearance = setupTabBarItemAppearance()
let appearance = UITabBarAppearance()
appearance.configureWithOpaqueBackground()
appearance.backgroundColor = Const.backgroundColor
appearance.stackedLayoutAppearance = tabBarItemAppearance
appearance.inlineLayoutAppearance = tabBarItemAppearance
appearance.compactInlineLayoutAppearance = tabBarItemAppearance
standardAppearance = appearance
// iOS15: we need to set
scrollEdgeAppearance = appearance
}
private func setupTabBarItemAppearance() -> UITabBarItemAppearance {
let tabBarItemAppearance = UITabBarItemAppearance()
// for normal
tabBarItemAppearance.normal.iconColor = Const.tintColor
tabBarItemAppearance.normal.titleTextAttributes = [NSAttributedString.Key.foregroundColor: Const.tintColor]
// for selected
tabBarItemAppearance.selected.iconColor = Const.selectedColor
tabBarItemAppearance.selected.titleTextAttributes = [NSAttributedString.Key.foregroundColor: Const.selectedColor]
return tabBarItemAppearance
}
}
</pre>
<br/>
UITabBarControllerの子となる各ViewControllerでは、次のようにtabBarItemを指定するのみです。<br/>
tabBarItemと配色をするAppearanceをコード上で分け、責務が分離できるので非常に見やすいコードとなりました。<br/>
<br/>
<pre class="prettyprint lang=swift linenums" style="overflow: auto; word-wrap: normal;">
final class FirstViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupTabBar()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
private func setupTabBar() {
tabBarItem = UITabBarItem(title: "Home", image: UIImage(systemName: "house"), selectedImage: UIImage(systemName: "house"))
}
}
final class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupTabBar()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
private func setupTabBar() {
tabBarItem = UITabBarItem(title: "Trash", image: UIImage(systemName: "trash"), selectedImage: UIImage(systemName: "trash"))
}
}
</pre>
baroqueworkdevhttp://www.blogger.com/profile/05590925357596481434noreply@blogger.com0tag:blogger.com,1999:blog-3403935679525313017.post-46453030930033548322021-09-26T19:27:00.003+09:002021-09-26T20:48:58.250+09:00リンクタップ可能でかつ選択不可なUITextViewを作る次なような、UITextViewを作成することにしました。<br/>
<br/>
1. リンクタップは可能である、その他のテキストは操作無反応とする<br/>
2. ダブルタップによるテキスト選択動作は不可である<br/>
3. ロングタップは不可である<br/>
<br/>
<br/>
<h2>UITextViewのカスタムViewを作成する</h2>
条件1から3まで満たすようなカスタムViewを作ります。<br/>
この方法は、Stackoverflowに書かれているものを参考にしました。<br/>
<a href="https://stackoverflow.com/questions/36198299/uitextview-disable-selection-allow-links" target="_blank">UITextView: Disable selection, allow links</a><br/>
<br/>
<br/>
<pre class="prettyprint lang=swift linenums" style="overflow: auto; word-wrap: normal;">
class UnselectableTextView: UITextView {
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
guard let position = closestPosition(to: point) else { return false }
guard let range = tokenizer.rangeEnclosingPosition(
position,
with: .character,
inDirection: UITextDirection(rawValue: UITextLayoutDirection.left.rawValue)
) else { return false }
let startIndex = offset(from: beginningOfDocument, to: range.start)
return attributedText.attribute(.link, at: startIndex, effectiveRange: nil) != nil
}
override func becomeFirstResponder() -> Bool {
return false
}
}
</pre>
<br/>
<br/>
override func point() にて、タップした位置のテキストがリンクであるかどうか判定し、override func becomeFirstResponder() にてダブルタップとロングタップを抑制しています。<br/>
ViewControllerに実装するときは、次のようになります。<br/>
<br/>
<br/>
<pre class="prettyprint lang=swift linenums" style="overflow: auto; word-wrap: normal;">
class ViewController: UIViewController {
@IBOutlet weak var textView: UnselectableTextView!
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
}
private func setupViews() {
textView.delegate = self
textView.isEditable = false
textView.isSelectable = true
textView.backgroundColor = .clear
let attributedString = NSMutableAttributedString(string: "this textview has a link test.")
attributedString.addAttribute(.link, value: "https://www.google.com", range: NSRange(location: 20, length: 4))
textView.attributedText = attributedString
}
}
extension UIViewController: UITextViewDelegate {
public func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
print(URL.absoluteString)
return false
}
}
</pre>
<br/>
<br/>
<h2>ロングタップをハンドリングする</h2>
上記で作成したUnselectableTextViewに、独自の UILongPressGestureRecognizer を組み込もうとしたが断念。<br/>
UITextViewの GestureRecognizer とConflictしてしまい、良い感じで実装できずでした。<br/>
代替案として、UnselectableTextViewの後ろにUIViewを配置して、このUIViewにてロングタップをハンドリングさせます。<br/>
<br/>
<br/>
<pre class="prettyprint lang=swift linenums" style="overflow: auto; word-wrap: normal;">
class ViewController: UIViewController {
private func setupGesture() {
let longPressGesture = UILongPressGestureRecognizer(
target: self,
action: #selector(ViewController.longPress(_:))
)
longPressGesture.delegate = self
backgroundView.addGestureRecognizer(longPressGesture)
}
}
extension UIViewController: UIGestureRecognizerDelegate {
@objc func longPress(_ sender: UILongPressGestureRecognizer) {
print(sender.state.rawValue.description)
}
}
</pre>
baroqueworkdevhttp://www.blogger.com/profile/05590925357596481434noreply@blogger.com0tag:blogger.com,1999:blog-3403935679525313017.post-52032414038482071142021-07-05T00:13:00.001+09:002021-09-26T20:49:43.537+09:00画面幅より短い場合はCenterさせるUIScrollViewを作成する次のようなViewを条件を満たすViewを作りたくなったので実装してみました。</b>
<div>
<ul style="text-align: left;">
<li>画面幅より短い場合は、Center表示させる</li>
<li>画面幅より長い場合は、左寄せで表示し横スクロール可能である</li>
</ul>
</div>
<br />
<div>
完成イメージは次のような感じです。
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbUcr73vf2ciKFXDih1Xe0uI2FEi7rlE1B-zIf2qApOpmmHF81_QFohQPNnTlWMz4uE5T3oGSTOqR_n6tRMO2JF70EOUq7wjTpO4tsAAwEQN4Jfi88q2Zpv_A9mxXfNx6cDH8jXxIq7z4J/s872/UIScrollView_sample1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="578" data-original-width="872" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbUcr73vf2ciKFXDih1Xe0uI2FEi7rlE1B-zIf2qApOpmmHF81_QFohQPNnTlWMz4uE5T3oGSTOqR_n6tRMO2JF70EOUq7wjTpO4tsAAwEQN4Jfi88q2Zpv_A9mxXfNx6cDH8jXxIq7z4J/s320/UIScrollView_sample1.png" width="320" /></a></div></div>
<h2>Viewの構成</h2>
Viewの構成は、UIScrollViewの中にコンテンツ用のUIViewを入れるのみです。
<br />
<pre class="prettyprint lang=swift linenums" style="overflow: auto; word-wrap: normal;">
private let scrollView = UIScrollView()
private let contentView = UIView()
private func setupViews() {
scrollView.addSubview(contentView)
addSubview(scrollView)
}
</pre>
<br />
実際に表示させたいViewは、次のようにfunc setup(_)メソッドを使って挿入させることを想定しています。<br />
<br />
<pre class="prettyprint lang=swift linenums" style="overflow: auto; word-wrap: normal;">
func setup(_ view: UIView) {
contentView.addSubview(view)
view.snp.makeConstraints {
$0.edges.equalTo(contentView)
}
}
</pre>
<br />
<br />
<br />
<h2>制約をセットする</h2>
scrollViewの制約は、通常通り親Viewに対して同じ大きさになるよう制約をセットします。<br />
contentViewの制約は工夫が必要で、親Viewに対して横Centerの指定とleftに対してgreaterThanOrEqualToを指定します。<br />
この2つの制約によりCenter表示が可能になります。<br />
<pre class="prettyprint lang=swift linenums" style="overflow: auto; word-wrap: normal;">
private func setupConstraint() {
scrollView.snp.makeConstraints {
$0.edges.equalToSuperview()
}
contentView.snp.makeConstraints {
$0.height.equalTo(scrollView.frameLayoutGuide)
// 横幅より小さい場合は、Center
// 横幅より大きい場合は、通常の左寄せ + スクロール
$0.top.right.bottom.equalTo(scrollView.contentLayoutGuide)
$0.left.equalTo(scrollView.contentLayoutGuide).priority(.low)
$0.left.greaterThanOrEqualTo(scrollView.contentLayoutGuide)
$0.centerX.greaterThanOrEqualToSuperview()
}
}
</pre>
<br />
<br />
ScrollのContent領域が定まらないというLayoutのwarningが発生したため、次の制約を追加します。
<pre class="prettyprint lang=swift linenums" style="overflow: auto; word-wrap: normal;">
$0.left.equalTo(scrollView.contentLayoutGuide).priority(.low)
</pre>
<br />
<br />
<br />
<h2>完成形のコード</h2>
上記の全てを組み合わせると、次のようなコードになります。
<pre class="prettyprint lang=swift linenums" style="overflow: auto; word-wrap: normal;">
final class HorizontalScrollView: UIView {
private let scrollView = UIScrollView()
private let contentView = UIView()
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
setupConstraint()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupViews()
setupConstraint()
}
private func setupViews() {
scrollView.addSubview(contentView)
addSubview(scrollView)
}
private func setupConstraint() {
scrollView.snp.makeConstraints {
$0.edges.equalToSuperview()
}
contentView.snp.makeConstraints {
$0.height.equalTo(scrollView.frameLayoutGuide)
// aligin center when content width < frame width
// aligin left when content width > frame width
$0.top.right.bottom.equalTo(scrollView.contentLayoutGuide)
$0.left.equalTo(scrollView.contentLayoutGuide).priority(.low)
$0.left.greaterThanOrEqualTo(scrollView.contentLayoutGuide)
$0.centerX.greaterThanOrEqualToSuperview()
}
}
func setup(_ view: UIView) {
contentView.addSubview(view)
view.snp.makeConstraints {
$0.edges.equalTo(contentView)
}
}
}
</pre>
baroqueworkdevhttp://www.blogger.com/profile/05590925357596481434noreply@blogger.com0tag:blogger.com,1999:blog-3403935679525313017.post-41324192719193444572019-08-17T13:49:00.002+09:002021-09-26T20:49:57.732+09:00UIScreenEdgePanGestureRecognizerをプログラムで実装する<h2>ViewControllerに実装する</h2>
</br>
storyboardではなく、プログラムでEdgeスワイプを実装してみました。</br>
UIScreenEdgePanGestureRecognizerをViewController.viewに追加して、selectorを使ってメソッドでハンドリングします。</br>
</br>
</br>
<pre class="prettyprint lang=swift linenums" style="overflow: auto; word-wrap: normal;">
class ViewController: UIViewController {
private let closer = SwipeEdgeCloser()
override func viewDidLoad() {
super.viewDidLoad()
let edgePan = UIScreenEdgePanGestureRecognizer(target: self, action: #selector(handleScreenEdgeSwiped))
edgePan.edges = .left
view.addGestureRecognizer(edgePan)
}
@objc func handleScreenEdgeSwiped(_ recognizer: UIScreenEdgePanGestureRecognizer) {
if recognizer.state == .recognized {
print("Screen edge swiped!")
}
}
}
</pre>
</br>
</br>
</br>
<h2>クラスに実装する</h2>
</br>
ViewControllerがファットになりそうなので、クラス化してみました。処理はほぼ同様で、selectorを新しく作成したSwipeLeftEdgePanGestureのメソッドを指定しています。
</br>
</br>
<pre class="prettyprint lang=swift linenums" style="overflow: auto; word-wrap: normal;">
class ViewController: UIViewController {
private let closer = SwipeLeftEdgePanGesture()
override func viewDidLoad() {
super.viewDidLoad()
view.addGestureRecognizer(closer.panGesture)
}
}
final class SwipeLeftEdgePanGesture {
let panGesture: UIScreenEdgePanGestureRecognizer
init() {
panGesture = UIScreenEdgePanGestureRecognizer()
panGesture.edges = .left
panGesture.addTarget(self, action: #selector(SwipeLeftEdgePanGesture.screenEdgeSwiped))
}
@objc private func screenEdgeSwiped(_ recognizer: UIScreenEdgePanGestureRecognizer) {
if recognizer.state == .recognized {
print("SwipeLeftEdgePanGesture: Screen edge swiped!")
}
}
}
</pre>
baroqueworkdevhttp://www.blogger.com/profile/05590925357596481434noreply@blogger.com0tag:blogger.com,1999:blog-3403935679525313017.post-79490410117349653732016-11-30T22:56:00.002+09:002016-11-30T22:56:47.098+09:00はじめてのTensorFlow 「Variables: 生成、初期化、保存と復元」その3原文:<br />
Variables: Creation, Initialization, Saving, and Loading<br />
<a href="https://www.tensorflow.org/versions/r0.12/how_tos/variables/index.html#variables-creation-initialization-saving-and-loading">https://www.tensorflow.org/versions/r0.12/how_tos/variables/index.html#variables-creation-initialization-saving-and-loading</a><br />
<br />
<br />
<H4>Saving and Restoring</H4>
モデルの保存/復元はtf.train.Saverオブジェクトを使用してください。コンストラクタはすべてのグラフ操作、特定のリスト、グラフ内の変数の保存/復元が可能です。Saverオブジェクトは、これらのopsを実行するメソッドを提供し、チェックポイントファイルの書き込みまたは読み取りのパスを指定します。<br />
<br />
<br />
<H4>Checkpoint Files</H4>
変数はバイナリファイルに保存され、大まかに、変数名からテンソル値までのマップを含みます。<br />
Saverオブジェクトを作成するときは、チェックポイントファイル内の変数の名前を任意に選択できます。 デフォルトでは、変数ごとにVariable.nameプロパティの値が使用されます。<br />
<br />
チェックポイント内の変数を理解するには、inspect_checkpointライブラリ、特にprint_tensors_in_checkpoint_file関数を使用します。<br />
<br />
<br />
<H4>Saving Variables</H4>
tf.train.Saver()を使用してSaverを作成し、モデル内のすべての変数を管理します。<br />
<br />
<pre class="brush: py;">
# Create some variables.
v1 = tf.Variable(..., name="v1")
v2 = tf.Variable(..., name="v2")
...
# Add an op to initialize the variables.
init_op = tf.global_variables_initializer()
# Add ops to save and restore all the variables.
saver = tf.train.Saver()
# Later, launch the model, initialize the variables, do some work, save the
# variables to disk.
with tf.Session() as sess:
sess.run(init_op)
# Do some work with the model.
..
# Save the variables to disk.
save_path = saver.save(sess, "/tmp/model.ckpt")
print("Model saved in file: %s" % save_path)
</pre>
<br />
<br />
<H4>Restoring Variables</H4>
同じSaverオブジェクトが変数の復元に使用されます。 ファイルから変数を復元するときは、あらかじめそれらを初期化する必要はありません。<br />
<br />
<br />
<pre class="brush: py;">
# Create some variables.
v1 = tf.Variable(..., name="v1")
v2 = tf.Variable(..., name="v2")
...
# Add ops to save and restore all the variables.
saver = tf.train.Saver()
# Later, launch the model, use the saver to restore variables from disk, and
# do some work with the model.
with tf.Session() as sess:
# Restore variables from disk.
saver.restore(sess, "/tmp/model.ckpt")
print("Model restored.")
# Do some work with the model
...
</pre>
<br />
<br />
<H4>Choosing which Variables to Save and Restore</H4>
tf.train.Saver()に引数を渡さない場合、グラフのすべての変数がSaverによって処理されます。 それぞれの変数は、変数の作成時に渡された名前で保存されます。<br />
チェックポイントファイル内の変数の名前を明示的に指定すると便利なことがあります。 たとえば、 "weights"という名前の変数を持つモデルを訓練し、その値を "params"という名前の新しい変数にリストアすることができます。<br />
<br />
また、モデルによって使用される変数のサブセットの保存または復元のみが有用な場合もあります。 たとえば、5層のニューラルネットを訓練して、6層の新しいモデルを訓練し、以前訓練されたモデルの5つの層のパラメータを新しいモデルの最初の5つの層に復元する必要があります。<br />
<br />
tf.train.Saver()コンストラクタにPythonのdictionaryを渡すことで、保存する名前と変数を簡単に指定できます。keysは使用する名前、valueは管理する変数です。<br />
<br />
ノート:<br />
モデル変数の異なるサブセットを保存して復元する必要がある場合は、必要な数のセーバーオブジェクトを作成できます。 同じ変数を複数の保存オブジェクトにリストすることができます。その値は、saver restore()メソッドが実行されたときにのみ変更されます。<br />
<br />
詳細は、tf.initialize_variablesを参照してください。
<pre class="brush: py;">
# Create some variables.
v1 = tf.Variable(..., name="v1")
v2 = tf.Variable(..., name="v2")
...
# Add ops to save and restore only 'v2' using the name "my_v2"
saver = tf.train.Saver({"my_v2": v2})
# Use the saver object normally after that.
...
</pre>
baroqueworkdevhttp://www.blogger.com/profile/05590925357596481434noreply@blogger.com0tag:blogger.com,1999:blog-3403935679525313017.post-18466578421822073402016-11-30T02:52:00.002+09:002016-11-30T02:52:30.524+09:00はじめてのTensorFlow 「Variables: 生成、初期化、保存と復元」その2原文:<br />
Variables: Creation, Initialization, Saving, and Loading<br />
<a href="https://www.tensorflow.org/versions/r0.12/how_tos/variables/index.html#variables-creation-initialization-saving-and-loading">https://www.tensorflow.org/versions/r0.12/how_tos/variables/index.html#variables-creation-initialization-saving-and-loading</a><br />
<br />
<br />
<H4>Initialization</H4>
変数の初期化は、他の操作を行う前に明確に実行する必要があります。モデルを使用する前にすべての変数を初期化を実行して下さい。<br />
<br />
チェックポイントファイルから変数の値を復元することもできます(以下を参照)<br />
変数を初期化するには、tf.global_variables_initializer()を使用します。<br />
モデルを完全に生成してセッションを起動した後にのみ、そのオペレーションを実行してください。<br />
<br />
<br />
<pre class="brush: py;">
# Create two variables.
weights = tf.Variable(tf.random_normal([784, 200], stddev=0.35),
name="weights")
biases = tf.Variable(tf.zeros([200]), name="biases")
...
# Add an op to initialize the variables.
init_op = tf.global_variables_initializer()
# Later, when launching the model
with tf.Session() as sess:
# Run the init operation.
sess.run(init_op)
...
# Use the model
...
</pre>
<br />
<br />
<H4>Initialization from another Variable</H4>
他の変数の初期値で変数を初期化することも可能です。tf.global_variables_initializerによって追加された演算子はすべての変数を並列に初期化するので、必要なときは注意して下さい。<br />
<br />
他の変数の値から変数を初期化は変数のinitialized_value()プロパティを使います。初期化された値は、新しい変数の初期値として直接使用することも、他のテンソルとして使用して新しい変数の値を計算することもできます。<br />
<br />
<pre class="brush: py;">
# Create a variable with a random value.
weights = tf.Variable(tf.random_normal([784, 200], stddev=0.35),
name="weights")
# Create another variable with the same value as 'weights'.
w2 = tf.Variable(weights.initialized_value(), name="w2")
# Create another variable with twice the value of 'weights'
w_twice = tf.Variable(weights.initialized_value() * 2.0, name="w_twice")
</pre>
<br />
<br />
<br />
<H4>Custom Initialization</H4>
tf.global_variables_initializerは、モデル内のすべての変数を初期化するための演算子を追加します。 また、初期化する変数の明示的なリストを渡すこともできます。 変数が初期化されているかどうかのチェックなど、その他のオプションについては変数ドキュメントを参照してください。
baroqueworkdevhttp://www.blogger.com/profile/05590925357596481434noreply@blogger.com0tag:blogger.com,1999:blog-3403935679525313017.post-58879129386770643712016-11-30T02:18:00.001+09:002016-11-30T02:18:43.161+09:00はじめてのTensorFlow 「Variables: 生成、初期化、保存と復元」その1原文:<br />
Variables: Creation, Initialization, Saving, and Loading<br />
https://www.tensorflow.org/versions/r0.12/how_tos/variables/index.html#variables-creation-initialization-saving-and-loading<br />
<br />
<br />
<H4>Variables: 生成、初期化、保存と復元</H4>
モデルを列挙にする際、パラメータを保持、更新するためにVariables(以後、変数)を使います。Variablesは行列としてメモリ上に存在します。変数は明示的に初期化する必要があり、トレーニング中かつトレーニング後はディスク上に可能です。保存された値を後で復元して、モデルを実行または分析することができます。<br />
<br />
このドキュメントでは、次のTensorFlowクラスを参照しています。 APIの完全な説明についてはリファレンスマニュアルのリンクをクリックしてください:<br />
The <a href="https://www.tensorflow.org/versions/r0.12/api_docs/python/state_ops.html#Variable">tf.Variable class</a>.<br />
The <a href="https://www.tensorflow.org/versions/r0.12/api_docs/python/state_ops.html#Saver">tf.train.Saver class</a>.<br />
<br />
<br />
<H4> Creation</H4>
変数を生成する際、初期値としてTensrに値をVariable()コンストラクタに渡します。<br />
TensorFlowは、定数やランダムな値から初期化するためによく使われるテンソルを生成するopsのコレクションを提供します。<br />
<br />
これらすべての操作では、テンソルの形状を指定する必要があります。 その形状は自動的に変数の形になります。 変数は一般的に固定された形状を持ちますが、TensorFlowは変数を再構成する高度なメカニズムを提供します。<br />
<br />
<pre class="brush: py;">
# Create two variables.
weights = tf.Variable(tf.random_normal([784, 200], stddev=0.35),
name="weights")
biases = tf.Variable(tf.zeros([200]), name="biases")
</pre>
<br />
tf.Variable()をコールすると、グラフにopsが追加されます。<br />
・保持:値を保持する<br />
・初期化:変数を初期化する。実際にはtf.assignで操作する。<br />
・初期値:biases変数のゼロ初期化など。この例ではグラフに追加もしている<br />
<br />
tf.Variable()の値が返す値は、Pythonクラスtf.Variableのインスタンスです。<br />
<br />
<br />
<br />
<H4>Device placement</H4>
変数は、生成するときに特定のデバイスに固定することができます。tf.deviceを使用します。<br />
<br />
<pre class="brush: py;">
# Pin a variable to CPU.
with tf.device("/cpu:0"):
v = tf.Variable(...)
# Pin a variable to GPU.
with tf.device("/gpu:0"):
v = tf.Variable(...)
# Pin a variable to a particular parameter server task.
with tf.device("/job:ps/task:7"):
v = tf.Variable(...)
</pre>
<br />
変数をmutateする操作、v.assign()やtf.train.Optimizer内でパラメータを更新する操作は同じデバイス上で実行する必要があります。互換性のないデバイス配置指定は、これらの操作を生成するときに無視されます。<br />
<br />
複製された設定で実行する場合、デバイス配置は特に重要です。<br />
複製されたされたモデルのデバイス設定を簡素化できるデバイス機能の詳細は、tf.train.replica_device_setterを参照してください。<br />
baroqueworkdevhttp://www.blogger.com/profile/05590925357596481434noreply@blogger.com0tag:blogger.com,1999:blog-3403935679525313017.post-38598913815574087562016-11-29T00:46:00.000+09:002016-11-29T00:46:18.253+09:00はじめてのTensorFlow PyCharm編PyCharmを起動して新しいプロジェクオを作成、interpreterでtensoflowの環境を選択します。<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDjuvKlIxJASxjiwuRCleIBUe5uCZXKbMls3dlXPaNhWWYdySdyvtEMnJXkAywoK4ETAPYHzDrAH7-HKlH63PQbQ3DSaXBYpfCmFNTPNSR_VUF5nK3asfHFdtK_mgvCNxyVpuVLWLHRyA/s1600/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588+2016-11-28+22.26.20.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDjuvKlIxJASxjiwuRCleIBUe5uCZXKbMls3dlXPaNhWWYdySdyvtEMnJXkAywoK4ETAPYHzDrAH7-HKlH63PQbQ3DSaXBYpfCmFNTPNSR_VUF5nK3asfHFdtK_mgvCNxyVpuVLWLHRyA/s400/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588+2016-11-28+22.26.20.png" width="400" height="236" /></a></div>
<br />
<br />
プロジェクト作成に成功すると、External LibrariesにTensorFlowに必要なライブラリが入った状態になります。<br />
TensorFlow用のpyファイルを作成して、コーディングします。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjL0YgaibJEUu2BVS6oVvJm6N9QQmEA1tAdJH1hHWoYO9vN7e7UxN0F89GOOdkI-JVxt5hqD_tJWZdeBS5T6FWk64aBrgH-sZrYg8-OYtH6WEIXsFxTFZ5lM6Y9DxdFfCkmksf37-s2tZA/s1600/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588+2016-11-28+22.28.09.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjL0YgaibJEUu2BVS6oVvJm6N9QQmEA1tAdJH1hHWoYO9vN7e7UxN0F89GOOdkI-JVxt5hqD_tJWZdeBS5T6FWk64aBrgH-sZrYg8-OYtH6WEIXsFxTFZ5lM6Y9DxdFfCkmksf37-s2tZA/s400/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588+2016-11-28+22.28.09.png" width="400" height="175" /></a></div>
<br />
<br />
Runを押して、プログラムを実行。
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFvH2h4DYZavptE0xukoKenCDx3BKugq6ZipoN3ZNPK2IOVMADaSTOl8xOHPPmhF9QZG7lrK4yXV5U7JDSpbxUYdmgKWk0WmUtq7zQbYDCrvMeN7Sa5X-gmhgrKdFYW8cfMzxNHYp5Ec4/s1600/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588+2016-11-28+22.28.45.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFvH2h4DYZavptE0xukoKenCDx3BKugq6ZipoN3ZNPK2IOVMADaSTOl8xOHPPmhF9QZG7lrK4yXV5U7JDSpbxUYdmgKWk0WmUtq7zQbYDCrvMeN7Sa5X-gmhgrKdFYW8cfMzxNHYp5Ec4/s400/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588+2016-11-28+22.28.45.png" width="400" height="124" /></a></div>
<br />
<br />
実行結果はPyCharm上で確認できます。Terminalで実行するより、PyCharmなどのIDEで開発する方が効率が良いです。
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaLAujeYfvx9tfdy65PdNTq6zqwuexmbEaRCHFC41jTnjhaXXMEv3N4iKqClmepMgrHFAzO0jdanIw-GEacsrK0dCepYvjFIIg207B1620ikMgIKhvdWvivt3vGsfdmidpF5GfYEddpYA/s1600/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588+2016-11-28+22.29.06.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaLAujeYfvx9tfdy65PdNTq6zqwuexmbEaRCHFC41jTnjhaXXMEv3N4iKqClmepMgrHFAzO0jdanIw-GEacsrK0dCepYvjFIIg207B1620ikMgIKhvdWvivt3vGsfdmidpF5GfYEddpYA/s400/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588+2016-11-28+22.29.06.png" width="400" height="174" /></a></div>baroqueworkdevhttp://www.blogger.com/profile/05590925357596481434noreply@blogger.com0tag:blogger.com,1999:blog-3403935679525313017.post-32082984707675064132016-09-25T09:44:00.000+09:002016-09-25T09:44:28.818+09:00はじめてのTensorFlow 動作確認編<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoz8ldGjhSque68vqkJnJgM2w9ImXs6Vh50PLNUsMd-eDS6JZltRnl8w45TkgIzOnjfO0R0pwAbTZyYlqtE24CrKARXjGx2q4iznP9dLlkGVbsdSUtqJ9YeVG1vyNJJ6AOopgEFPces7M/s1600/tensorflow.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoz8ldGjhSque68vqkJnJgM2w9ImXs6Vh50PLNUsMd-eDS6JZltRnl8w45TkgIzOnjfO0R0pwAbTZyYlqtE24CrKARXjGx2q4iznP9dLlkGVbsdSUtqJ9YeVG1vyNJJ6AOopgEFPces7M/s320/tensorflow.jpg" width="320" height="121" /></a></div>
r.0.10の動作確認用のソースコードが用意されています。<br />
インストール後に実行してみましょう。<br />
https://www.tensorflow.org/versions/r0.10/get_started/index.html<br />
<br />
<br />
<h4>動作確認</h4>
動作確認用のPythonファイルを作成します。<br />
<pre class="brush: bash;">
(tensorflow)$ touch tf_test.py
(tensorflow)$ vi tf_test.py
</pre>
<br />
<br />
サンプルソースはy = x * a + bの、aとbの値を求めるプログラムです。<br />
<pre class="brush: py;">
import tensorflow as tf
import numpy as np
# Create 100 phony x, y data points in NumPy, y = x * 0.1 + 0.3
x_data = np.random.rand(100).astype(np.float32)
y_data = x_data * 0.1 + 0.3
# Try to find values for W and b that compute y_data = W * x_data + b
# (We know that W should be 0.1 and b 0.3, but TensorFlow will
# figure that out for us.)
W = tf.Variable(tf.random_uniform([1], -1.0, 1.0))
b = tf.Variable(tf.zeros([1]))
y = W * x_data + b
# Minimize the mean squared errors.
loss = tf.reduce_mean(tf.square(y - y_data))
optimizer = tf.train.GradientDescentOptimizer(0.5)
train = optimizer.minimize(loss)
# Before starting, initialize the variables. We will 'run' this first.
init = tf.initialize_all_variables()
# Launch the graph.
sess = tf.Session()
sess.run(init)
# Fit the line.
for step in range(201):
sess.run(train)
if step % 20 == 0:
print(step, sess.run(W), sess.run(b))
# Learns best fit is W: [0.1], b: [0.3]
</pre>
<br />
<br />
プログラムの実行。<br />
<pre class="brush: bash;">
(tensorflow)$ python tf_test.py
</pre>
<pre class="brush: bash;">
(0, array([-0.08499005], dtype=float32), array([ 0.5096584], dtype=float32))
(20, array([ 0.03381674], dtype=float32), array([ 0.33253124], dtype=float32))
(40, array([ 0.08111919], dtype=float32), array([ 0.30928054], dtype=float32))
(60, array([ 0.09461367], dtype=float32), array([ 0.30264756], dtype=float32))
(80, array([ 0.09846338], dtype=float32), array([ 0.30075532], dtype=float32))
(100, array([ 0.09956162], dtype=float32), array([ 0.30021548], dtype=float32))
(120, array([ 0.09987493], dtype=float32), array([ 0.30006149], dtype=float32))
(140, array([ 0.09996434], dtype=float32), array([ 0.30001754], dtype=float32))
(160, array([ 0.09998985], dtype=float32), array([ 0.30000502], dtype=float32))
(180, array([ 0.09999709], dtype=float32), array([ 0.30000144], dtype=float32))
(200, array([ 0.09999915], dtype=float32), array([ 0.30000043], dtype=float32))
</pre>
<br />
0.1 と 0.3の近似値が算出されている!!<br />
baroqueworkdevhttp://www.blogger.com/profile/05590925357596481434noreply@blogger.com0tag:blogger.com,1999:blog-3403935679525313017.post-41417221904378565352016-09-25T09:21:00.001+09:002016-09-25T09:21:43.431+09:00はじめてのTensorFlow インストール編<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHarAVvWFPHp-i1ExRVrRtdCdU0UNC-bzPVQAbY31nLHvtm87gS_xAHSQZgjQTrjWGfIoDyIRUuscr_7WGNMCetcrWZyi_S5q7Kk1h518wmcHFUSrVaN9BY0TeKg27PJkiGrfG9ZCKeqI/s1600/tensorflow.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHarAVvWFPHp-i1ExRVrRtdCdU0UNC-bzPVQAbY31nLHvtm87gS_xAHSQZgjQTrjWGfIoDyIRUuscr_7WGNMCetcrWZyi_S5q7Kk1h518wmcHFUSrVaN9BY0TeKg27PJkiGrfG9ZCKeqI/s320/tensorflow.jpg" width="320" height="121" /></a></div>
<h4>TensorFlow</h4>
以下、公式ページを参考にしました。</br>
<a href="https://www.tensorflow.org/versions/master/get_started/os_setup.html#virtualenv-installation">https://www.tensorflow.org/versions/master/get_started/os_setup.html#virtualenv-installation</a></br>
<br/>
記事の動作確認環境はr.0.10、MacOS 10.11.6です。</br>
<br/>
<br/>
<h4>ここで使う知識</h4>
pip</br>
<a href="https://ja.wikipedia.org/wiki/Pip">https://ja.wikipedia.org/wiki/Pip</a></br>
Virtualenv</br>
<a href="https://virtualenv.pypa.io/en/stable">https://virtualenv.pypa.io/en/stable</a>/</br>
<br/>
<br/>
<h4>TensorFlowの環境構築</h4>
所要時間5分以内で完了します。</br>
</br>
pipとVirtualenvのインストール</br>
<pre class="brush: bash;">
$ sudo easy_install pip
$ sudo pip install --upgrade virtualenv
</pre>
Virtualenvの環境を構築する。ディレクトリは~/tensorflowを指定。<br/>
<pre class="brush: bash;">
$ virtualenv --system-site-packages ~/tensorflow
</pre>
Virtualenvで構築した環境に入る。<br/>
<pre class="brush: bash;">
$ source ~/tensorflow/bin/activate
</pre>
pipを用いてTensorFlowをインストールする。<br/>
TensorFlowをCPUのみで動作させる場合と、GPUを有効にする場合でURLが異なるので注意。<br/>
<pre class="brush: bash;">
# Mac OS X, CPU only, Python 2.7:
(tensorflow)$ export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/mac/cpu/tensorflow-0.10.0-py2-none-any.whl
# Mac OS X, GPU enabled, Python 2.7:
(tensorflow)$ export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/mac/gpu/tensorflow-0.10.0-py2-none-any.whl
</pre>
インストールの実行<br/>
<pre class="brush: bash;">
(tensorflow)$ pip install --upgrade $TF_BINARY_URL
</pre>
<br/>
成功すると次のメッセージが表示される。<br/>
<pre class="brush: bash;">
Successfully installed funcsigs-1.0.2 mock-2.0.0 numpy-1.11.1 pbr-1.10.0 protobuf-3.0.0b2 six-1.10.0 tensorflow-0.10.0
</pre>
<br/>
以上でインストールは終了です。<br/>
<br/>
TensorFlow環境からexitする場合は次のコマンドを実行する。<br/>
<pre class="brush: bash;">
(tensorflow)$ deactivate</pre>baroqueworkdevhttp://www.blogger.com/profile/05590925357596481434noreply@blogger.com0tag:blogger.com,1999:blog-3403935679525313017.post-64204132101941664272016-05-22T19:14:00.000+09:002016-05-22T19:14:25.955+09:00iOSでFirebase Analyticsを使う<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhoertjRgcIRFmXyVZUh0wnJ0OlEyzUz9FGfogIJzL0_hS0gi4YpkwCfPGH1L5MK9mVSoLxWa2Rb-Mxyo_5QaeI-w1wlkYFv2fu2Bys96lUbwHlLnVJNcstvvAZM-dDsczeDqABRjIXPBw/s1600/unnamed-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhoertjRgcIRFmXyVZUh0wnJ0OlEyzUz9FGfogIJzL0_hS0gi4YpkwCfPGH1L5MK9mVSoLxWa2Rb-Mxyo_5QaeI-w1wlkYFv2fu2Bys96lUbwHlLnVJNcstvvAZM-dDsczeDqABRjIXPBw/s400/unnamed-2.png" /></a></div>
<br />
(引用元:https://firebase.google.com)
<br />
<h4>セットアップ</h4>
公式Page<br />
<a href="https://firebase.google.com/docs/ios/setup">https://firebase.google.com/docs/ios/setup</a><br />
<br />
CocoaPodを利用してインストールします。<br />
podの初期設定を実行。<br />
<pre class="brush: java;">
pod init
</pre>
Podfile を開いて以下を追加します。<br />
<pre class="brush: java;">
pod 'Firebase/Core'
</pre>
podのインストールを実行します。<br />
<pre class="brush: java;">
pod install
</pre>
<br />
Analytics以外の機能を使用するには、次の公式サイトに記載されているpodを追加しましょう。<br />
<a href="https://firebase.google.com/docs/ios/setup#available_pods">https://firebase.google.com/docs/ios/setup#available_pods</a><br />
<br />
<br />
<pre class="brush: java;">
import UIKit
import Firebase
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// init Firebase
FIRApp.configure()
return true
}
}
</pre>
<br />
<br />
<h4>イベント送信方法</h4>
以下、宣言済みのイベントがあり、ヘッダーファイルに定義されているので参照。<br />
イベント:FIReventNames.h <br />
パラメータ:FIRParameterNames.h<br />
<br />
<br />
<pre class="brush: java;">
FIRAnalytics.logEventWithName(kFIREventSelectContent, parameters: [
kFIRParameterContentType:"cont",
kFIRParameterItemID:"1"
])
</pre>
<br />
<br />
<h4>
ユーザープロパティ</h4>
アプリのユーザーをカテゴライズすることが可能です。<br />
<pre class="brush: java;">
FIRAnalytics.setUserPropertyString(food, forName: "favorite_food")
</pre>
baroqueworkdevhttp://www.blogger.com/profile/05590925357596481434noreply@blogger.com0tag:blogger.com,1999:blog-3403935679525313017.post-17911977090644235822016-05-22T11:18:00.000+09:002016-05-22T18:44:09.094+09:00AndroidでFirebase Analyticsを使う<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhoertjRgcIRFmXyVZUh0wnJ0OlEyzUz9FGfogIJzL0_hS0gi4YpkwCfPGH1L5MK9mVSoLxWa2Rb-Mxyo_5QaeI-w1wlkYFv2fu2Bys96lUbwHlLnVJNcstvvAZM-dDsczeDqABRjIXPBw/s1600/unnamed-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhoertjRgcIRFmXyVZUh0wnJ0OlEyzUz9FGfogIJzL0_hS0gi4YpkwCfPGH1L5MK9mVSoLxWa2Rb-Mxyo_5QaeI-w1wlkYFv2fu2Bys96lUbwHlLnVJNcstvvAZM-dDsczeDqABRjIXPBw/s400/unnamed-2.png" /></a></div>
<br />
(引用元:https://firebase.google.com)
<br />
<br />
<h4>
セットアップ</h4>
セットアップ方法は、必ず最新の情報を参照ください。<br />
公式Page<br />
<a href="https://firebase.google.com/docs/android/setup">https://firebase.google.com/docs/android/setup</a><br />
<br />
プロジェクトのbuild.gradleに次のclasspathを追加する。<br />
<pre class="brush: java;">buildscript {
dependencies {
classpath 'com.google.gms:google-services:3.0.0'
}
}
</pre>
<br />
<br />
appのbuild.gradleに次のpluginを追加する。<br />
<br />
<pre class="brush: java;">// Add to the bottom of the file
apply plugin: 'com.google.gms.google-services'
</pre>
<br />
Firebase Analyticsを使用するには、次のdependenciesを追加する。<br />
<pre class="brush: java;">dependencies {
compile 'com.google.firebase:firebase-analytics:9.0.0'
}
</pre>
<br />
Analytics以外の機能を使用するには、次の公式サイトに記載されているdependenciesを追加しましょう。<br />
<a href="https://firebase.google.com/docs/android/setup#available_libraries">https://firebase.google.com/docs/android/setup#available_libraries</a>
<br />
<br />
<h4>
イベントの送信</h4>
<h5>
イベントの種類</h5>
以下、宣言済みのイベントがあります。<br />
<a href="https://support.google.com/firebase/answer/6317485?hl=en&ref_topic=6317484">https://support.google.com/firebase/answer/6317485?hl=en&ref_topic=6317484</a><br />
FirebaseAnalytics.Event<br />
<a href="https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event">https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event</a><br />
<br />
<ul>
<li>Automatically collected events</li>
<li>Events: All apps</li>
<li>Events: Retail/Ecommerce</li>
<li>Events: Jobs, Education, Local Deals, Real Estate</li>
<li>Events: Travel (Hotel/Air)</li>
<li>Events: Games</li>
<li>Automatically collected user properties</li>
</ul>
<br />
<br />
<br />
宣言済みのイベント以外にも、オリジナルのイベントの追加が可能ですが以下の制限があります。<br />
<br />
<ul>
<li>イベントは500種類まで</li>
<li>イベント名はユニークな名前であること</li>
<li>イベント名のPrefixに"firebase_"を付けない(SHOULD)</li>
<li>イベント名は32文字の、アルファベットとunderscores( _ )のみ</li>
</ul>
<br />
<br />
<h5>
パラメータ</h5>
各イベントに25個までのパラメータを付加することが可能です。定義済みのパラメータがあります。<br />
FirebaseAnalytics.Param<br />
<a href="https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Param">https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Param</a><br />
<br />
イベント同様、オリジナルのパラメータの追加が可能です。
<ul>
<li>パラメータ名は24文字の、アルファベットとunderscores( _ )のみ</li>
<li>パラメータの値は36文字まで</li>
<li>Prefixに"firebase_"を付けない(SHOULD)</li>
</ul>
<br />
<br />
<h5>
イベントの送信方法</h5>
Bundleにパラメータを詰めて、logEventにイベント名とBundleを渡すだけ送信可能です。<br />
<br />
<pre class="brush: java;">FirebaseAnalytics mFirebaseAnalytics = FirebaseAnalytics.getInstance(this);
Bundle bundle = new Bundle();
bundle.putString(FirebaseAnalytics.Param.ITEM_ID, id);
bundle.putString(FirebaseAnalytics.Param.ITEM_NAME, name);
bundle.putString(FirebaseAnalytics.Param.CONTENT_TYPE, "image");
mFirebaseAnalytics.logEvent(FirebaseAnalytics.Event.SELECT_CONTENT, bundle);
</pre>
<br />
<br />
<h4>
ユーザープロパティ</h4>
アプリのユーザーをカテゴライズすることが可能です。<br />
FirebaseAnalytics.UserProperty<br />
<a href="https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.UserProperty">https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.UserProperty</a><br />
<br />
アプリごとに25種類までユーザー属性を追加することができます。<br />
<br />
<ul>
<li>ユーザープロパティ名は24文字まで、アルファベットとunderscores( _ )のみ</li>
<li>ユーザープロパティの値は36文字まで</li>
<li>Prefixに"firebase_"を付けない(SHOULD)</li>
</ul>
<br />
<br />
<pre class="brush: java;">
FirebaseAnalytics mFirebaseAnalytics = FirebaseAnalytics.getInstance(this);
mFirebaseAnalytics.setUserProperty("favorite_food", mFavoriteFood);
</pre>
<br />baroqueworkdevhttp://www.blogger.com/profile/05590925357596481434noreply@blogger.com0tag:blogger.com,1999:blog-3403935679525313017.post-86618885616922682932016-04-28T06:13:00.002+09:002021-09-26T20:50:38.796+09:00[RxSwift] SectionありのDataSourceを生成する本家のサンプルにも記載されている通り、RxDataSourcesを使用する方法があります。<br />
<a href="https://github.com/ReactiveX/RxSwift/tree/master/RxExample/RxDataSources">https://github.com/ReactiveX/RxSwift/tree/master/RxExample/RxDataSources</a><br />
<br />
RxDataSources:<br />
<a href="https://github.com/RxSwiftCommunity/RxDataSources">https://github.com/RxSwiftCommunity/RxDataSources</a><br />
<br />
Podfileに記載するなりしてインストールしてください。<br />
<br />
<h4>SectionありのdataSourceを生成する</h4>
RxTableViewSectionedReloadDataSourceを使用します。SectionModelでSectionに表示するelementを指定します。<br />
<br />
<pre class="prettyprint lang=swift linenums" style="overflow: auto; word-wrap: normal;">
let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, String>>()
let items = Observable.just([
SectionModel(model: "First section", items: [
"aaaa",
"bbbb",
]),
SectionModel(model: "Second section", items: [
"cccc",
"dddd",
])
])
</pre>
<br />
<br />
<h4>セルの生成</h4>
以下のように、indexPathとelementが引数に入っているのでコールバック内でセルを生成する。<br />
<pre class="prettyprint lang=swift linenums" style="overflow: auto; word-wrap: normal;">
dataSource.configureCell = { (_, tableView, indexPath, element) in
let cell = tv.dequeueReusableCellWithIdentifier("Cell")!
cell.textLabel?.text = "\(element) @ row \(indexPath.row)"
return cell
}
</pre>
<br />
<br />
<h4>データとTablewViewをBindする</h4>
<pre class="prettyprint lang=swift linenums" style="overflow: auto; word-wrap: normal;">
items
.bindTo(tableView.rx_itemsWithDataSource(dataSource))
.addDisposableTo(disposeBag)
</pre>
<br />
<br />
<h4>ヘッダーのカスタマイズ</h4>
UITableViewDelegateで処理する必要があります。<br />
以下のように、ヘッダー用のViewとHightを返すモジュールを実装します。<br />
<pre class="prettyprint lang=swift linenums" style="overflow: auto; word-wrap: normal;">
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let view = UITableViewHeaderFooterView()
view.textLabel?.text = dataSource.sectionAtIndex(section).model ?? ""
return view
}
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 44
}
</pre>baroqueworkdevhttp://www.blogger.com/profile/05590925357596481434noreply@blogger.com0tag:blogger.com,1999:blog-3403935679525313017.post-22667667629952802972016-04-16T19:07:00.001+09:002021-09-26T20:51:01.327+09:00[RxSwift] UITextFieldの入力テキストと通知ViewModel側にObserverを作成する。ObserverでText更新を受け取ってViewModelの処理を行う。<br />
<br />
<br />
<pre class="prettyprint lang=swift linenums" style="overflow: auto; word-wrap: normal;">
let disposeBag = DisposeBag()
var account:Variable<String> = Variable<String>("")
// 参照方法
account.value
</pre>
<br />
<br />
UITextFieldのrx_textとViewModelのObserverをBindする。<br />
Textが更新されると、ViewModel側のObserverまで届く。<br />
<pre class="prettyprint lang=swift linenums" style="overflow: auto; word-wrap: normal;">
let disposeBag = DisposeBag()
accountTextField.rx_text
.bindTo(viewModel.accountSubject.asObserver())
.addDisposableTo(disposeBag)
</pre>
<br />
<br />
プログラム上でTextを更新する場合(ストリームに流す場合)、sendActionsForControlEventsでValueChangedイベントをUITextFieldに流し込む。<br />
<pre class="prettyprint lang=swift linenums" style="overflow: auto; word-wrap: normal;">
accountTextField.text = "test"
accountTextField.sendActionsForControlEvents(.ValueChanged)
</pre>
baroqueworkdevhttp://www.blogger.com/profile/05590925357596481434noreply@blogger.com0tag:blogger.com,1999:blog-3403935679525313017.post-89413331653338795172016-04-15T07:54:00.002+09:002021-09-26T20:51:40.356+09:00[RxSwift] UITableViewでカスタムセルを使用するUITableViewで表示するデータをVariableで宣言するし、データの更新はvalueにセットする。<br />
<br />
<pre class="prettyprint lang=swift linenums" style="overflow: auto; word-wrap: normal;">
let items = Variable<[Item]>([])
// 新しいデータで更新
self.items.value = updateItems
</pre>
<br />
UITableViewにデータをBindingするには、rx_itemsWithCellIdentifierをコールする。<br />
カスタムセルを使用する場合は、第二引数のcellTypeにセットが必要です。<br />
<br />
<pre class="prettyprint lang=swift linenums" style="overflow: auto; word-wrap: normal;">
viewModel?.items
.asDriver()
.drive(tableView.rx_itemsWithCellIdentifier("CustomTableViewCell",cellType: CustomTableViewCell.self)) { row, item, cell in
cell.item = item
cell.tag = row
}
.addDisposableTo(disposeBag)
</pre>
<br />
<br />
セルの選択はrx_itemSelectedを使用します。
<pre class="prettyprint lang=swift linenums" style="overflow: auto; word-wrap: normal;">
tableView.rx_itemSelected
.subscribeNext { [unowned self](indexPath) in
self.tableView.deselectRowAtIndexPath(indexPath, animated: true)
// get cell
let cell: CustomTableViewCell = self.tableView.cellForRowAtIndexPath(indexPath)! as! CustomTableViewCell
}
.addDisposableTo(disposeBag)
</pre>
<br />
<h4>EditingStyleのセット</h4>
rx_itemsWithCellIdentifierを使用すると、DefaultでEditting可能になってしまいます。<br />
不要の場合はUITableViewCellEditingStyle.Noneをセットしましょう。<br />
<br />
<pre class="prettyprint lang=swift linenums" style="overflow: auto; word-wrap: normal;">
func tableView(tableView: UITableView, editingStyleForRowAtIndexPath: NSIndexPath) -> UITableViewCellEditingStyle {
return UITableViewCellEditingStyle.None
}
</pre>
baroqueworkdevhttp://www.blogger.com/profile/05590925357596481434noreply@blogger.com0tag:blogger.com,1999:blog-3403935679525313017.post-76398441981171803262016-04-12T07:10:00.001+09:002016-04-15T07:54:28.094+09:00[Swift]循環参照(相互参照)によるメモリリークMVVM移行時にクラス分けを行った結果、ViewControllerのメモリリークが発生しました。deinitが呼ばれず期待動作となりませんでした。<br />
<br />
<br />
<h4>循環参照(相互参照)によるメモリリーク</h4>
以下のような状態になると、メモリリークが発生します。<br />
何も考えずにViewModelのDelegateを追加、DelegateにViewControllerをセットでメモリリーク発生<br />
<br />
<br />
<h4>解決策</h4>
弱参照になるようにDelegate用プロトコルをclass継承し、weakで保持できるように対応。<br />
<br />
<br />
protocolにclass継承追加<br />
<pre class="brush: java;">
public protocol ViewModelDelegate : class{
}
</pre>
<br />
<br />
weak追加
<pre class="brush: java;">
weak var delegate: ViewModelDelegate?
</pre>
baroqueworkdevhttp://www.blogger.com/profile/05590925357596481434noreply@blogger.com0tag:blogger.com,1999:blog-3403935679525313017.post-70164457950470598742016-04-03T09:59:00.000+09:002016-04-03T09:59:56.919+09:00[Swift]UIButtonでPressed状態の装飾を行うAndroidならSelectorのxmlファイルを作成すればボタンの状態に合わせた装飾できます。<br />
Swiftではボタンの文字などは次のfuncで設定可能です。<br />
<br />
<pre class="brush: java;">
func setTitle(_ title: String?,forState state: UIControlState)
func setTitleColor(_ color: UIColor?,forState state: UIControlState)
func setTitleShadowColor(_ color: UIColor?,forState state: UIControlState)
func setBackgroundImage(_ image: UIImage?,forState state: UIControlState)
</pre>
<br />
layerなどで装飾をする場合は、次のようにhighlightedのdidSetで変更可能です。<br />
<br />
<pre class="brush: java;">
@IBDesignable class MyUIButton: UIButton {
@IBInspectable var borderColor : UIColor = UIColor.blackColor()
@IBInspectable var borderHighLightedColor : UIColor = UIColor.clearColor()
override internal func awakeFromNib() {
super.awakeFromNib()
}
override var highlighted: Bool{
didSet{
if (highlighted) {
self.layer.borderColor = borderHighLightedColor.CGColor
} else {
self.layer.borderColor = borderColor.CGColor
}
}
}
// Attributes Inspectorで設定した値を反映
override func drawRect(rect: CGRect) {
self.layer.borderColor = borderColor.CGColor
}
}
</pre>
baroqueworkdevhttp://www.blogger.com/profile/05590925357596481434noreply@blogger.com0tag:blogger.com,1999:blog-3403935679525313017.post-34491667616251460552016-03-27T15:42:00.000+09:002016-03-27T15:42:15.200+09:00[Swift] Keyboardの表示に合わせてScrollViewの高さを変更するAndroidの場合、Keyboardが表示されると自動的にアプリケーションWIndowがリサイズされてKeyboardに被らないように表示されます。<br />
iOSでは自前で実装する必要があります。<br />
<br />
以下は、ScrollVIewを利用したサンプルです。<br />
Keyboardが表示されたタイミングで、ScrollViewのcontentInsetとscrollIndicatorInsetsを変更します。<br />
<br />
<pre class="brush: java;">
class AdjustScrollViewControll : NSObject{
var scrollView : UIScrollView?
init(scrollView : UIScrollView){
self.scrollView = scrollView
}
func addObservers(){
let notificationCenter = NSNotificationCenter.defaultCenter()
notificationCenter.addObserver(self, selector: #selector(self.willShowNotification(_:)), name: UIKeyboardWillShowNotification, object: nil)
notificationCenter.addObserver(self, selector: #selector(self.willHideNotification(_:)), name: UIKeyboardWillHideNotification, object: nil)
}
func removeObserviers(){
NSNotificationCenter.defaultCenter().removeObserver(self)
}
func willShowNotification(notification: NSNotification) {
let info = notification.userInfo
let infoNSValue = info![UIKeyboardFrameEndUserInfoKey] as! NSValue
let kbSize = infoNSValue.CGRectValue().size
let contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height + 8, 0.0)
scrollView!.contentInset = contentInsets
scrollView!.scrollIndicatorInsets = contentInsets
}
func willHideNotification(notification: NSNotification) {
let contentInsets = UIEdgeInsetsMake(0.0, 0.0, 0.0, 0.0)
scrollView!.contentInset = contentInsets
scrollView!.scrollIndicatorInsets = contentInsets
}
}
</pre>
<br />
<br />
ViewControllerからは次のようにコールします。<br />
<pre class="brush: java;">
class HiddingKeyboardViewController: UIViewController,UITextFieldDelegate {
@IBOutlet weak var scrollView: UIScrollView!
var adjustTextFieldControll : AdjustScrollViewControll?
override func viewDidLoad() {
super.viewDidLoad()
adjustTextFieldControll = AdjustScrollViewControll(scrollView: scrollView)
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
adjustTextFieldControll!.addObservers()
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
adjustTextFieldControll!.removeObserviers()
}
}
</pre>baroqueworkdevhttp://www.blogger.com/profile/05590925357596481434noreply@blogger.com0tag:blogger.com,1999:blog-3403935679525313017.post-16932324866613369182016-03-26T08:35:00.003+09:002016-03-26T08:35:56.268+09:00[Swift]アスペクト比を維持しつつUIImageViewのサイズを変更するTableViewCellの実装で、次のような画面幅に合わせた画像を表示する際に困ったのでメモ。<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqzkF7jilVkvAQOYivlmKhTHa9Pv-rwuFIVFNhyphenhyphenchhu0NFslCASXmk_WXYftertKLjsphfVugQOXur5lYSv5uOjnfKD1oSpkLYH-Fzw4y2rCAqnpRlhyphenhyphenqxzl3FHRZRKueLdkWHN-fl6V0/s1600/Simulator+Screen+Shot+2016.03.26+8.30.17.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqzkF7jilVkvAQOYivlmKhTHa9Pv-rwuFIVFNhyphenhyphenchhu0NFslCASXmk_WXYftertKLjsphfVugQOXur5lYSv5uOjnfKD1oSpkLYH-Fzw4y2rCAqnpRlhyphenhyphenqxzl3FHRZRKueLdkWHN-fl6V0/s320/Simulator+Screen+Shot+2016.03.26+8.30.17.png" /></a></div>
<br />
<br />
Androidでは次のように指定することで、高さが自動で確定することができました。<br />
<br />
<pre class="brush: xml;">
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"/>
</pre><br />
が、iOSでは簡単にはできず、カスタムViewを作ることで解決しました。<br />
UIImageViewのimageがセットされる際に、アスペクト比を計算してConstraintを確定しているだけです。<br />
<br />
<pre class="brush: java;">
class AspectAdjuestUIImageView: UIImageView {
internal var aspectConstraint : NSLayoutConstraint? {
didSet {
if oldValue != nil {
self.removeConstraint(oldValue!)
}
if aspectConstraint != nil {
self.addConstraint(aspectConstraint!)
}
}
}
override var image: UIImage?{
willSet{
let aspect = newValue!.size.width / newValue!.size.height
aspectConstraint = NSLayoutConstraint(item: self, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.Height, multiplier: aspect, constant: 0.0)
}
}
}
</pre>baroqueworkdevhttp://www.blogger.com/profile/05590925357596481434noreply@blogger.com0tag:blogger.com,1999:blog-3403935679525313017.post-63963397400098391152016-03-23T10:20:00.000+09:002016-03-23T10:20:30.896+09:00SwiftなCocoa Touch FrameworkでCommonCryptoを使う for iOS9.3SwiftでCommonCryptoを使用する際、libcommonCryptoライブラリのリンクエラーが出たので対応方法を。<br />
<br />
手順は次のようになります。<br />
<br />
<ol>
<li>CommonCryptoというディレクトリを作成</li>
<li>module.mapというファイルを作成</li>
<li>プロジェクト設定のBuild Settings -> Swift Compiler - Search Paths -> Import Pathsに、上記のCommonCryptoディレクトリを指定</li>
</ol>
<br />
<br />
<br />
次はサンプルです。
module.mapにSDK内のヘッダファイルのPathを記述します。<br />
<br />
<pre class="brush: java;">module CommonCrypto [system] {
header "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/include/CommonCrypto/CommonCrypto.h"
- link "CommonCrypto"
export *
}
</pre>
<br />
link "CommonCrypto"を削除します。baroqueworkdevhttp://www.blogger.com/profile/05590925357596481434noreply@blogger.com0tag:blogger.com,1999:blog-3403935679525313017.post-73322900253988972802016-03-19T12:29:00.000+09:002016-03-19T12:29:53.150+09:00[Swift]TableViewのSection HeaderをカスタムViewに置き換えるTableViewのSection HeaderをカスタムViewに置き換える方法です。<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGcV3Iqh_Hs319oFN349lcMoZpJ1rf8T7UV1_w2T0Z4FX4DxT99MRgyqfATzHZqFFWgeg0TenhnpQBXyCl8oeMXeYe1Ni8uunBA2-jhwdfvr2EpQzOoIU-WtrBg2pZECoZGsITJ4IHNQI/s1600/Simulator+Screen+Shot+2016.03.19+10.05.30.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGcV3Iqh_Hs319oFN349lcMoZpJ1rf8T7UV1_w2T0Z4FX4DxT99MRgyqfATzHZqFFWgeg0TenhnpQBXyCl8oeMXeYe1Ni8uunBA2-jhwdfvr2EpQzOoIU-WtrBg2pZECoZGsITJ4IHNQI/s400/Simulator+Screen+Shot+2016.03.19+10.05.30.png" /></a></div>
<br />
<br />
<h4>UITableViewHeaderFooterViewのサブクラスを作成</h4>
UITableViewHeaderFooterViewを継承したクラスを作成します。<br />
<br />
<pre class="brush: java;">
import UIKit
class CustomTableViewHeaderFooterView: UITableViewHeaderFooterView {
@IBOutlet weak var headerView: UIView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
}
</pre>
<br />
<br />
<h4>Nibファイルを作成</h4>
Header用のViewを作成します。<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyhcm8Ck-d7rJ6WQMz6nJDge0tyEFIJOBOGvMqg9cPqV6T3RDa1JCpU7MCJrsnRsOcYObYzDsoBCYhagCSc5BIWnNadqN30cOl4DLD6XvVF6xuO3cjhvcA8DckwlHHcQWVcafJUT5DGgc/s1600/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588+2016-03-19+12.19.25.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyhcm8Ck-d7rJ6WQMz6nJDge0tyEFIJOBOGvMqg9cPqV6T3RDa1JCpU7MCJrsnRsOcYObYzDsoBCYhagCSc5BIWnNadqN30cOl4DLD6XvVF6xuO3cjhvcA8DckwlHHcQWVcafJUT5DGgc/s400/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588+2016-03-19+12.19.25.png" /></a></div><br />
<br />
<br />
<h4>TableViewにセット</h4>
Cellと同様にtableView.registerNibでセットしておきます。tableView:viewForHeaderInSectionでロードして戻り値とします。<br />
注意点としては、ViewのサイズがAutolayoutになっていないので、frame.sizeでサイズ指定します。<br />
<br />
<pre class="brush: java;">
class ViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
let nib:UINib = UINib(nibName: "CustomTableViewHeaderFooterView", bundle: nil)
tableView.registerNib(nib, forHeaderFooterViewReuseIdentifier: "CustomTableViewHeaderFooterView")
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
// header height
override func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 44
}
// header view
override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let header :CustomTableViewHeaderFooterView = self.tableView.dequeueReusableHeaderFooterViewWithIdentifier("CustomTableViewHeaderFooterView") as! CustomTableViewHeaderFooterView
header.headerView.frame.size = CGRectMake(0, 0, tableView.frame.size.width, 44).size
return header
}
}
</pre>baroqueworkdevhttp://www.blogger.com/profile/05590925357596481434noreply@blogger.com0tag:blogger.com,1999:blog-3403935679525313017.post-87673882674215514112016-03-19T08:14:00.002+09:002016-03-19T08:14:52.158+09:00[Swift]iOSでGoogle Places API for iOSを使うGoogle Places APIをiOSで使う方法です。<br />
公式サイトはこちらです。<br />
<br />
<a href="https://developers.google.com/places/ios-api/?hl=ja">Google Places API for iOS</a><br />
https://developers.google.com/places/ios-api/?hl=ja<br />
<br />
<br />
<h4>
SDKの追加</h4>
SDKはCocoaPodで公開されています。Podfileに次の項目を追加します。<br />
<br />
<pre class="brush: java;">source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.1'
pod 'GoogleMaps'
</pre>
Podfileに追加の上、installを実行するだけで完了です。<br />
<br />
<pre class="brush: java;">pod install
</pre>
<br />
<br />
<h4>
API Keyの取得</h4>
Google Developer Consoleにて、APIの設定が必要です。次はAPI Keyを取得するまでの手順です。<br />
<br />
<ul>
<li>プロジェクトを作成</li>
<li>Google Places API for iOS と Google Maps SDK for iOSを有効にする</li>
<li>認証情報からiOSキーを作成(BundleIDの登録が必要です)</li>
<li>API Keyを取得する</li>
</ul>
<br />
<br />
<br />
<h4>
API Keyのセット</h4>
AppDelegate.swiftで、GMSServicesクラスにAPI Keyのセットします。<br />
import GoogleMapsが必要です。<br />
<br />
<pre class="brush: java;"> func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
GMSServices.provideAPIKey("YOUR_API_KEY")
return true
}
</pre>
<br />
<br />
<h4>
Place Autocompleteの使い方</h4>
ViewControllerからGMSAutocompleteViewControllerを起動します。<br />
オートコンプレートによるPlaceの選択結果は、GMSAutocompleteViewControllerDelegateで受け取ることができます。<br />
<br />
<pre class="brush: java;">extension ViewController: GMSAutocompleteViewControllerDelegate {
func startGooglePlacesAutocomplete(){
let autocompleteController = GMSAutocompleteViewController()
autocompleteController.delegate = self
self.presentViewController(autocompleteController, animated: true, completion: nil)
}
// Handle the user's selection.
func viewController(viewController: GMSAutocompleteViewController, didAutocompleteWithPlace place: GMSPlace) {
print("Place name: ", place.name)
print("Place address: ", place.formattedAddress)
print("Place attributions: ", place.attributions)
self.dismissViewControllerAnimated(true, completion: nil)
}
func viewController(viewController: GMSAutocompleteViewController, didFailAutocompleteWithError error: NSError) {
// TODO: handle the error.
print("Error: ", error.description)
}
// User canceled the operation.
func wasCancelled(viewController: GMSAutocompleteViewController) {
self.dismissViewControllerAnimated(true, completion: nil)
}
// Turn the network activity indicator on and off again.
func didRequestAutocompletePredictions(viewController: GMSAutocompleteViewController) {
UIApplication.sharedApplication().networkActivityIndicatorVisible = true
}
func didUpdateAutocompletePredictions(viewController: GMSAutocompleteViewController) {
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
}
}
</pre>
<br />
<br />
GMSAutocompleteViewControllerの起動が成功すると、次のような画面が表示されます。Placesの確定時にdidAutocompleteWithPlaceがコールされるので、引数:placeで値を受け取ります。
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYc8PXr-nrFcLay-yZnb1ESSJIXxjrQKOAxjmF-vW5EJq2-f7u3vDSJvV0FySnKnklMTdta1X_3WX5_mFMGM3hsI0mdJy6Jmn0cQUEvxhLn-AQo00-QNUIkfYLNc2jRW2t5bCe-8QwVvA/s1600/Simulator+Screen+Shot+2016.03.17+23.10.26.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYc8PXr-nrFcLay-yZnb1ESSJIXxjrQKOAxjmF-vW5EJq2-f7u3vDSJvV0FySnKnklMTdta1X_3WX5_mFMGM3hsI0mdJy6Jmn0cQUEvxhLn-AQo00-QNUIkfYLNc2jRW2t5bCe-8QwVvA/s400/Simulator+Screen+Shot+2016.03.17+23.10.26.png" /></a></div>
baroqueworkdevhttp://www.blogger.com/profile/05590925357596481434noreply@blogger.com0tag:blogger.com,1999:blog-3403935679525313017.post-37160544407315659172016-03-05T05:30:00.000+09:002016-03-05T05:30:09.563+09:00BottomSheetの実装方法 via Android Design Support Library 23.2Android Design Support Library 23.2でBottomSheetが追加されました。<br />
GoogleMapなどに用いられている画面が作成可能になりました。<br />
実装はCoordinatorLayoutの子Viewに対して、Behaviorを指定するだけとシンプルです。<br />
<br />
AOSPのソースコード : <a href="https://android.googlesource.com/platform/frameworks/support/+/master/design/src/android/support/design/widget/BottomSheetBehavior.java">BottomSheetBehavior.java</a>
<br />
<br />
<h4>
Layoutのサンプル</h4>
BottomSheetはBehaviorを指定することで実装します。<br />
CoordinatorLayoutの子ViewとしてLayoutを追加して、BottomSheet化したいLayoutに対してapp:layout_behavior="@string/bottom_sheet_behavior"を指定します。<br />
<br />
<pre class="brush: xml;">
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
app:layout_behavior="@string/bottom_sheet_behavior"
app:behavior_peekHeight="240dp"
app:behavior_hideable="false"
android:background="@android:color/white">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="BottomSheet Sample"/>
</LinearLayout>
</android.support.design.widget.CoordinatorLayout>
</pre>
次のパラメータの指定が可能です。<br />
<br />
<ul>
<li>app:behavior_peekHeight</li>
<ul>
<li>BottomSheetの最小表示サイズ</li>
</ul>
<li>app:behavior_hideable</li>
<ul>
<li>下スクロールで完全に非表示にするかどうか。非表示後に上スクロールで表示可能</li>
</ul>
</ul>
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2XYqeq_NHOzfoOuPqrnffFU9IkidqfST49EJ5dRwPs5dntBYSAiyeXdeCwfzhZQV729b6hjyfvF9teNauZ2ZdbJFIXfSAnDTNBwvXgL1t-aH9hHzUs8TLMucMmkxYq9vfDrWCVNFAiyI/s1600/device-2016-03-05-052719.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2XYqeq_NHOzfoOuPqrnffFU9IkidqfST49EJ5dRwPs5dntBYSAiyeXdeCwfzhZQV729b6hjyfvF9teNauZ2ZdbJFIXfSAnDTNBwvXgL1t-aH9hHzUs8TLMucMmkxYq9vfDrWCVNFAiyI/s320/device-2016-03-05-052719.png" /></a></div>baroqueworkdevhttp://www.blogger.com/profile/05590925357596481434noreply@blogger.com0tag:blogger.com,1999:blog-3403935679525313017.post-52613427659657861262016-03-02T09:21:00.001+09:002016-03-02T09:21:46.058+09:00[Swift]NavigationControllerのNavigationBarの表示 / 非表示方法NavigationControllerを使っている際、画面上部のBarを表示したくないことがあります。</br>
viewDidLoad()で表示/非表示するのではなく、viewWillAppear()とviewWillDisappear()で表示切り替えを行い、</br>
画面遷移後のControllerに処理させないようにしました。</br>
</br>
<pre class="brush: java;">
override func viewWillAppear(animated: Bool) {
self.navigationController?.navigationBarHidden = true
}
override func viewWillDisappear(animated: Bool) {
self.navigationController?.navigationBarHidden = false
}
</pre>baroqueworkdevhttp://www.blogger.com/profile/05590925357596481434noreply@blogger.com0tag:blogger.com,1999:blog-3403935679525313017.post-7021110473248759702016-02-13T16:57:00.000+09:002016-02-13T16:57:22.598+09:00Android M Fingerprint APIについて調べてみた関西モバイル研究会で発表した内容です。<br />
<br />
Android M FingerPrint(public)<br />
http://www.slideshare.net/baroqueworksdev/android-m-finger-printpublic<br />
<br />
<h4>簡易クラス図</h4>
アプリからHAL層までの簡易クラス図です。<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiq7GbXmz4FeAmV6b_nUmBpFjqQnek8VnJ4uf0FH49_QDbDv-vivSWDhnPnc60VTgOv0HrsrqwNHICXGixybMK_Qw5B71rh11R2XkXSd5Gji32G9PEE9rdXT0MGPwQP3Hxn9nJkFffuvaw/s1600/%25E3%2582%25AF%25E3%2583%25A9%25E3%2582%25B9%25E5%259B%25B31.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiq7GbXmz4FeAmV6b_nUmBpFjqQnek8VnJ4uf0FH49_QDbDv-vivSWDhnPnc60VTgOv0HrsrqwNHICXGixybMK_Qw5B71rh11R2XkXSd5Gji32G9PEE9rdXT0MGPwQP3Hxn9nJkFffuvaw/s400/%25E3%2582%25AF%25E3%2583%25A9%25E3%2582%25B9%25E5%259B%25B31.png" /></a></div>
<br />
<br />
<h4>
アプリケーションから使用する場合</h4>
<ul>
<li>Context#getSystemService()でFragmentprintManagerにアクセス </li>
<li>コールバックで結果を受け取る</li>
</ul>
<br />
<h4>
FingerprintManagerクラス</h4>
<ul>
<li>アプリケーションから要求を受け取る</li>
<li>Android Frameworksのサービス群にある、FingerprintServiceに連携</li>
<li>認証結果をFingerprintServiceから受け取り、アプリケーションに通知する</li>
</ul>
<br />
<h4>
FingerprintServiceクラス</h4>
<ul>
<li>SystemServiceクラスを継承</li>
<li>Android Systemとして指紋認証機能を実行</li>
<li>Native(JNI->HAL)につなぐ</li>
<li>認証要求時にFingerprintDeamonクラスをとおして、Native層に通知
アプリケーションの突然死の際、unbindする(IBinder.DeathRecipient)</li>
</ul>
<br />
<h4>
JNI / HAL層</h4>
<ul>
<li>HAL層(ライブラリ)をとおして、Kernelに通知、ハードウェアの制御を行う</li>
<li>認証結果をKeystoreServiceに通知</li>
</ul>baroqueworkdevhttp://www.blogger.com/profile/05590925357596481434noreply@blogger.com0