2016年12月2日金曜日

iOSアプリのNavigationBarにメニューアイコンをつける

やりたいこと

下のスクリーンショットにあるようにNavigationBarにメニューを示すアイコンを表示したい

やり方

  • IoniconsSwiftを使ってアイコン画像をロード
  • ロードした画像を使ってUIBarButtonItemインスタンスを作成
  • インスタンスをnavigationItemにsetLeftBarButtonItemsしてやるだけ

実際のコード

override func viewDidLoad() {
    super.viewDidLoad()
    
    // NavigationBarの高さ × 0.8にアイコンのサイズを指定する
    let barHeight : Int = Int((self.navigationController?.navigationBar.frame.size.height)! * 0.8)
    
    // Ioniconよりメニューアイコン画像をロード
    let menuIcon = UIImage.imageWithIonicon(ionicon.AndroidMenu, color: UIColor.black, iconSize: CGFloat(barHeight), imageSize: CGSize(width: barHeight, height: barHeight))

    // アイコン画像からUIBarButtonItemを生成
    let leftNavEditButtonItem = UIBarButtonItem(image: menuIcon, style: .plain, target: self, action: #selector(MyViewController.doSomething))

    // UIBarButtonItemをセット
    self.navigationItem.setLeftBarButtonItems([leftNavEditButtonItem], animated: true)
}

2016年11月22日火曜日

UIButtonに渡すselfはinit後に渡そうね


最近、Androidを離れてSwiftでiOSアプリを作成していますが、次の点にはまったのでメモ 

やりたいこと

  •  イベントに応じてNavigationBarに動的にボタンを追加したり消したりする 


失敗した例

  • UIBarButtonItemのインスタンスを静的メンバとして保持しておく 
  • イベントに応じて保持しているインスタンスをセットしたりアンセットする 
class MyViewController: UIViewController {
    var rightNavEditButtonItem = UIBarButtonItem(barButtonSystemItem: .edit, target: self, action: #selector(MyViewController.test))
    
    func test() {
        print("hogehoge")
    }

    func occurSomeEvent(_ calendar: FSCalendar, didSelect date: Date) {
        self.navigationItem.setRightBarButtonItems([rightNavEditButtonItem!], animated: true)
    }
}

 これだと、UIBarButtonItemをnewしたタイミングで引数に渡しているselfが決まっていない(インスタンス化されていない)ため
コード的にエラーはなくともselectorで指定したメソッドが呼ばれない

 正しい例

  • UIBarButtonItemのインスタンスをインスタンスメンバとして保持しておく
  •  viewDidLoadなどselfが決まっている状態でUIBarButtonItemをnewする
  •  イベントに応じて保持しているインスタンスをセットしたりアンセットする
class MyViewController: UIViewController {
    var rightNavEditButtonItem : UIBarButtonItem?
    
    override func viewDidLoad() {
        rightNavEditButtonItem = UIBarButtonItem(barButtonSystemItem: .edit, target: self, action: #selector(MyViewController.test))
    }
    
    func test() {
        print("hogehoge")
    }

    func occurSomeEvent(_ calendar: FSCalendar, didSelect date: Date) {
        self.navigationItem.setRightBarButtonItems([rightNavEditButtonItem!], animated: true)
    }
}

2016年7月27日水曜日

Hot Ramenリリース

Hot Ramenというアプリをリリースしました。 お手持ちの画像に簡単に動く湯気を付けることができます。 画像はGIFとして保存できるのでTwitterなどへ投稿ができます。 料理以外にも人物の怒った姿を演出するのにも使えます。

ラーメンもホカホカ!
もう怒ったぞ!

2016年6月3日金曜日

バネの動きをするAction

Cocos2d-xでアプリを作っていて結局使わなかったがバネの動きを再現したActionを作ったので公開する。
 libcocos2dのソースの中に組み込んでやれば他のActionと同様に呼び出せるようになる。
ソースの中にdefineしてしまっているが自然長や初速度、摩擦係数などを変更すれば動きをもっと激しくすることも可能。

SpringMoveBy.h
#ifndef SPRINGMOVE_H_
#define SPRINGMOVE_H_

#include "cocos2d.h"

class CC_DLL SpringMoveBy : public cocos2d::ActionInterval
{
public:
    static SpringMoveBy* create(float duration, const cocos2d::Vec2& position, float mass, float k);

    //
    // Overrides
    //
    virtual SpringMoveBy* clone() const override;
    virtual SpringMoveBy* reverse(void) const override;
    virtual void startWithTarget(cocos2d::Node *target) override;
    /**
     * @param time In seconds.
     */
    virtual void update(float time) override;

CC_CONSTRUCTOR_ACCESS:
SpringMoveBy() {}
    virtual ~SpringMoveBy() {}

    /**
     * initializes the action
     * @param duration in seconds
     */
    bool initWithDuration(float duration, const cocos2d::Vec2& position, float mass, float k);

protected:
    cocos2d::Vec2 _startPosition;
    cocos2d::Vec2 _delta;
    float  _k;
    float   _mass;
    cocos2d::Vec2 _previousPos;
    float  _velocity;
    float  _accel;
    float  _position;

private:
    CC_DISALLOW_COPY_AND_ASSIGN(SpringMoveBy);
};

#endif /* SPRINGMOVE_H_ */

SpringMoveBy.cpp
#include "SpringMove.h"

USING_NS_CC;

//
// SpringMoveBy
//

#define GRAVITY 9.80665 // 重力加速度
#define NATURAL_LENGTH 0 // 自然長
#define FIRST_ACCEL GRAVITY // 初期加速度
#define FIRST_VEROCITY 60 // 初期速度
#define FIRST_POSITION 0 // 初期位置
#define FRICTION 0.996 // 摩擦係数

USING_NS_CC;

SpringMoveBy* SpringMoveBy::create(float duration, const Vec2& position, float mass, float k)
{
    SpringMoveBy *springMoveBy = new (std::nothrow) SpringMoveBy();
    springMoveBy->initWithDuration(duration, position, mass, k);
    springMoveBy->autorelease();

    return springMoveBy;
}

bool SpringMoveBy::initWithDuration(float duration, const Vec2& position, float mass, float k)
{
    CCASSERT(mass >=0, "Number of mass must be >= 0");

    if (ActionInterval::initWithDuration(duration) && mass>=0)
    {
        _delta = position;
        _mass = mass;
        _k = k;
        _velocity = FIRST_VEROCITY;
        _accel = FIRST_ACCEL;
        _position = FIRST_POSITION;

        return true;
    }

    return false;
}

SpringMoveBy* SpringMoveBy::clone() const
{
    // no copy constructor
    auto a = new (std::nothrow) SpringMoveBy();
    a->initWithDuration(_duration, _delta, _mass, _k);
    a->autorelease();
    return a;
}

void SpringMoveBy::startWithTarget(Node *target)
{
    ActionInterval::startWithTarget(target);
    _previousPos = _startPosition = target->getPosition();
}

void SpringMoveBy::update(float t) // _delta = 現在座標
{
    if (_target)
    {
     auto force = -_k * ( _position - NATURAL_LENGTH ) + _mass * GRAVITY;
     _accel = force / _mass;
     _velocity += _accel * t;
     _velocity = _velocity * FRICTION;
     _position += _velocity * t;

#ifdef CC_ENABLE_STACKABLE_ACTIONS
        Vec2 currentPos = _target->getPosition();

        Vec2 diff = currentPos - _previousPos;
        _startPosition = diff + _startPosition;

        Vec2 newPos = _startPosition;

        newPos = _startPosition + Vec2(0,_position);

        _target->setPosition(newPos);

        _previousPos = newPos;
#else
        _target->setPosition(_startPosition + Vec2(0, _position));
#endif // !CC_ENABLE_STACKABLE_ACTIONS

    }
}

SpringMoveBy* SpringMoveBy::reverse() const
{
    return SpringMoveBy::create(_duration, Vec2(-_delta.x, -_delta.y),
        _mass, _k);
}

動作例
 

2016年6月1日水曜日

Boomreastリリース

Cocos2d-xを使った画像編集アプリBoomreastをリリースしました。
当初の予定では選択箇所を揺らすことを目標にしていましたが、Cocos2d-xを使う限りでは思うような揺れが実現できませんでした。ひとまずリリースしたい!という思いで目標を下げまくった結果のアプリです・・・。

もし、何かご要望がありましたら是非ご意見頂けると嬉しいです。

編集前

編集後

2016年2月12日金曜日

Cocos2d-xのサンプルコードLens3DのJumpByが止まってしまう件

Cocos2d-xの勉強のためサンプルコードをいくつか動かしてみた。
※サンプルコードを動かす方法はこちら

その中でも興味を引いたのは19. Effects - Advanced にあったJumpy Lens3D


これを使えば面白いものが作れそう!

実際にどのような処理を行っているのかコードを読んだところ

 void Effect4::onEnter()
{
    EffectAdvanceBaseTest::onEnter();
    //Node* gridNode = NodeGrid::create();
    
    auto lens = Lens3D::create(10, Size(32,24), Vec2(100,180), 150);
    auto move = JumpBy::create(5, Vec2(380,0), 100, 4);
    auto move_back = move->reverse();
    auto seq = Sequence::create( move, move_back, nullptr);

    /* In cocos2d-iphone, the type of action's target is 'id', so it supports using the instance of 'Lens3D' as its target.
        While in cocos2d-x, the target of action only supports Node or its subclass,
        so we make an encapsulation for Lens3D to achieve that.
    */

    auto director = Director::getInstance();
    auto pTarget = Lens3DTarget::create(lens);
    // Please make sure the target been added to its parent.
    this->addChild(pTarget);
    //gridNode->addChild(pTarget);

    director->getActionManager()->addAction(seq, pTarget, false);
    
    _bgNode->runAction( lens );
}

Lens3Dのインスタンスを作成してそれをLens3DTargetというNodeを継承したクラスでラップしてActionManagerへ渡している。
このLens3DTargetは本当にただラップしているだけ、ソース上の説明にある通りActionの操作対象はNodeかそのサブクラスである必要があるためらしい。JumpByからsetPosition()とgetPosition()が呼ばれたらメンバであるLens3Dの同メソッドを呼んでいる。

class Lens3DTarget : public Node
{
public:
    virtual void setPosition(const Vec2& var)
    {
        _lens3D->setPosition(var);
    }
    
    virtual const Vec2& getPosition() const
    {
        return _lens3D->getPosition();
    }
    
    static Lens3DTarget* create(Lens3D* pAction)
    {
        Lens3DTarget* pRet = new (std::nothrow) Lens3DTarget();
        pRet->_lens3D = pAction;
        pRet->autorelease();
        return pRet;
    }
private:

    Lens3DTarget()
        : _lens3D(nullptr)
    {}

    Lens3D* _lens3D;
};

基本は分かったので試しにSeaquenceに渡すmoveを一つ増やしてみたところ、
追加分のmoveが動かない

理由も全く分からない。
そして、デバックを繰り返したところやっと理由が判明!

Lens3D::create(10, Size(32,24), Vec2(100,180), 150)

durationとして10を渡していたのが原因だった。

static Lens3D* create (float duration, 
const Size & gridSize, 
const Vec2 & position, 
float radius 
)
static
Create the action with center position, radius, a grid size and duration. 
Parameters
durationSpecify the duration of the Lens3D action. It's a value in seconds. 
gridSizeSpecify the size of the grid. 
positionSpecify the center position of the lens. 
radiusSpecify the radius of the lens. 


このdurationはこのLens3Dの存在時間を指定している。
この値はActionManagerはupdate()の中で操作しているactionが終わったかどうかをisDone()問いうメソッドを通して確認をしている。

このisDoneでは実際に経過した時間と指定したdurationを比べてdurationが経過した場合このActionは終わったとしてtrueを返すため、ActionManagerよりremoveAction(Lens3Dのインスタンス)でLens3Dが管理対象から外されてしまう。

そのため、一回のJumpByが5秒であるmoveの3回目は丁度タイミングよく実行されなくなる。


あー、ここまで調べるのに3日以上かかってしまった。。。





2016年1月11日月曜日

Cocos2d-xアプリをeclipseでデバッグする

忘れるといけないのでデバッグ方法をメモ

  • 参考ページ:
    • https://www.drjiro.com/game-engine/cocos2dx/cocos2d-x-native-debug-using-eclipse/
  • 環境:
    • MacOS 10.11.2
    • eclipse Mars.1 Release (4.5.1)
    • Cocos2d-x 3.9
  • 前提:
    • ソースの編集はeclipse上で行う
    • ソースのビルドはコンソール上で行う
    • アプリのデバッグはeclipse上で行う
  • 手順:
    • ビルドしたいプロジェクトを作成orどこかからダウンロード
    • 参考ページに従いcocosプロジェクト配下のproj.androidとcocos2dをeclipseのimportよりプロジェクトに追加
    • proj.androidを右クリック>プロパティ>Builders>CDT Builderにチェックが付いていたら外す
    • cocos2dのプロジェクトをビルド、同プロジェクトのbin配下にjava.jarができていることを確認
    • proj.androidのjni配下のApplication.mkを次の様になっているかを確認
APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -DCC_ENABLE_CHIPMUNK_INTEGRATION=1 -DCOCOS2D_DEBUG=1 -std=c++11 -fsigned-char

APP_ABI := armeabi
APP_PLATFORM := android-10

ifeq ($(NDK_DEBUG),1)
  APP_CPPFLAGS += -DCOCOS2D_DEBUG=1
  APP_OPTIM := debug
else
  APP_CPPFLAGS += -DNDEBUG
  APP_OPTIM := release
endif
    •  コンソールから次のコマンドを実行してビルド
cocos compile -p android -m debug –ndk-mode NDK_DEBUG=1
    •  それぞれのプロジェクトをrefresh
    • 右クリックでDebug As > Android Native Applicationを実行
  • 注意点:
    • デバッグするときに自作ソース以外でもでもブレークポイントが打てる様にするため、cocosのライブラリソースもデバッグ検索パスに含めた方が良い

    • ライブラリソースを追加する方法はproj.androidを右クリック>New Folderを選択選択時のダイアログの下部にあるAdvancedを選択、一番下のLink to alternate Locationを選択しライブラリソースが含まれるディレクトリを選択
    • proj.androidを右クリック> New Source Folderで既に追加したリンクフォルダを選択すればOK


参考ページの方法ではeclipse上でビルドも行う様な設定を行っていたが、なぜか自分の環境では上手くいかなったためビルドはコンソール上から実行する様にする。