なんとかするから、なんとかなる

エンジニア関係のことを書きます

Macで立てたlocalhostサーバーにiPhoneからアクセスする

English version below

はじめに

Macでテスト用サーバーを立ててiPhoneから簡易的に試したいときのTipsです。

前提

  • MaciPhoneは同一ネットワークに存在する

やりかた

  1. MacLocalhostサーバーを立てる
  2. localhost:{Port番号}でアクセスできることをsafariなどで確認する
  3. Mac [共有] からMacにアクセスするためのドメイン名を調べる({YOUR_PC_NAME}.local) f:id:hopita:20180930154815p:plain
  4. iPhonesafariなどで、ドメイン名({YOUR_PC_NAME}.local)にアクセス

完了です。

注意点1

Mac上のサーバーのポート番号に80番などよく使われるt番号を使用すると、MacFirewallにインバウンドを拒否されます。

回避方法としては、Mac [環境設定]-[セキュリティ&プライバシー] でFirewallをOFFにすればアクセスできますが、お勧めできません。

注意点2

iOSアプリのAPIサーバーとしてMaclocalhost サーバーを使用した場合、XcodeでATS(App Transport Security)をOFFにするのを忘れずに。

HTTPSでアクセスできる場合は問題無いです。

iOS Xcode10 で”CommonCrypto”のおかげでビルドエラーが出た時の対処法

English version below

はじめに

Xcode10来ましたね。

早速アップデートしてみると次のようなエラー。

CocoaPodsとかで、モジュールをプロジェクトに組み込んでいると出会いやすいっぽいです。

Redefinition of module "CommonCrypto"
error: could not build Objective-C module 'YOUR_MODULE'

読み進んでみると"CommonCrypto"が再定義されていると。

やったこと

1つ目

クリーンしてビルド

ー>失敗

2つ目

cocoapods pod update を実行

-> だめ

解決方法

対象のModuleに含まれる"CommonCrypto"フォルダを削除する。

どうやらCommonCryptoがXcode10から標準ライブラリに組み込まれるようになったらしいです。

その結果、既存のファイルとコンフリクトが発生、再定義とみなされるようです。

つらい。。。

iOS How to change a language settings while debugging on the devices

Introduction

This article shows how to change a language settings while debugging on the devices.

This method is useful for confirming the localized String or text including line breaks.

It won't need to change the language from settings anymore.

Solution

There are 2 steps for setting the language.

  1. [Open Edit scheme] f:id:hopita:20180910211338p:plain
  2. [Run]-[Application Language]-[System Language] and select a language you want to set. f:id:hopita:20180910211504p:plain

iOS 実機でデバッグするときに言語設定を変更する

English version is below.

はじめに

実機でデバッグ時に言語設定したい。

ローカライズがきちんとできているか、改行が正しくできているかなど確認するときに便利な方法です。

いちいちデバイス言語設定を変更する必要はありません。

解決方法

  1. Edit Schemeを開きます f:id:hopita:20180910211338p:plain
  2. [Run]のApplication Languageを"SystemLanguage"から好きな言語へ変更すればOKです。 f:id:hopita:20180910211504p:plain

iOS UIPageViewControllerで"Unbalanced calls to begin/end appearance transitions for" が出たときの対処方法

English version below

はじめに

UIPageViewControllerを早送りしたときに次のようなエラーが出ました。

Unbalanced calls to begin/end appearance transitions for 

そのときのコードはこんな感じです。

class MyPageViewController: UIPageViewController {
    var maxPage = 3
    var currentPage = 0

    ~~~~
    // SomeCode....
    ~~~~
}

extension MyPageViewController: UIPageViewControllerDataSource {
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
        guard self.currentPage > self.maxPage else { return nil }
        self.currentPage += 1
        
        let myAfterVC = MyAfterViewController()
        
        return myAfterVC
    }
    
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        guard self.currentPage >=  0 else { return nil }
        self.currentPage -= 1
        
        let myBeforeVC = MyBeforeViewController()
        
        return myBeforeVC
    }
}

原因

このエラーメッセージは、現在のUIViewControllerの表示が完了する前に、次のUIViewControllerへ遷移しようとしたときに出るようです。

解決方法

遷移中に再度遷移しないようにフラグをもたせました。

UIPageViewControllerDelegateを通して、で遷移の開始と終了を知ることができます。

したがって、コードでは次のようになります。

class MyPageViewController: UIPageViewController {
    var isTransitioning = false
    var maxPage = 3
    var currentPage = 0

    ~~~~
    // SomeCode....
    ~~~~
}

extension MyPageViewController: UIPageViewControllerDataSource {
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
        guard self.currentPage < self.maxPage, self.isTransitioning == false else { return nil }
        self.currentPage += 1
        
        let myAfterVC = MyAfterViewController()
        
        return myAfterVC
    }
    
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        guard self.currentPage >=  0, self.isTransitioning == false else { return nil }
        self.currentPage -= 1
        
        let myBeforeVC = MyBeforeViewController()
        
        return myBeforeVC
    }
}

extension MyPageViewController: UIPageViewControllerDelegate {
    func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
        self.isTransitioning = true
    }
    
    func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
        self.isTransitioning = false
    }
}

iOS UIPageViewController How to soleve the error message "Unbalanced calls to begin/end appearance transitions for" .

Introduction

When I tried to transition some pages so rapidly.

The Xcode shows an error message as below.

Unbalanced calls to begin/end appearance transitions for 

The code is below.

class MyPageViewController: UIPageViewController {
    var maxPage = 3
    var currentPage = 0

    ~~~~
    // SomeCode....
    ~~~~
}

extension MyPageViewController: UIPageViewControllerDataSource {
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
        guard self.currentPage > self.maxPage else { return nil }
        self.currentPage += 1
        
        let myAfterVC = MyAfterViewController()
        
        return myAfterVC
    }
    
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        guard self.currentPage >=  0 else { return nil }
        self.currentPage -= 1
        
        let myBeforeVC = MyBeforeViewController()
        
        return myBeforeVC
    }
}

Why

This error message is showed up when a new transition begin before an old old is not completed.

Solution

To prepend a new transition before an old one is not completed, I added a transitioning flag in MyPageViewController and change the value in UIPageVieController Delegate.

The true code is below.

class MyPageViewController: UIPageViewController {
    var isTransitioning = false
    var maxPage = 3
    var currentPage = 0

    ~~~~
    // SomeCode....
    ~~~~
}

extension MyPageViewController: UIPageViewControllerDataSource {
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
        guard self.currentPage < self.maxPage, self.isTransitioning == false else { return nil }
        self.currentPage += 1
        
        let myAfterVC = MyAfterViewController()
        
        return myAfterVC
    }
    
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        guard self.currentPage >=  0, self.isTransitioning == false else { return nil }
        self.currentPage -= 1
        
        let myBeforeVC = MyBeforeViewController()
        
        return myBeforeVC
    }
}


extension MyPageViewController: UIPageViewControllerDelegate {
    func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
        self.isTransitioning = true
    }
    
    func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
        self.isTransitioning = false
    }
}

iOS How to set the Autolayout programatically

Introduction

It was difficult and complexity to set the autolayout programatically when the AutoLyaout announced in iOS

NSLayoutConstraint(item: button, attribute: .leading, relatedBy: .equal, toItem: self.view, attribute: .leadingMargin, multiplier: 1.0, constant: 0.0).isActive = true

That was because I used to use Snapkit(OSS).

github.com

But, these days, it will easy to set the autolayout programatically.

I'll introduce the method of setting autolayout programatically.

How to set the autolayout programatically

For example, if you want to the view at the bottom of the self.view, you can easy to set it below.

Code

let button = UIButton()
self.view.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
button.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
button.heightAnchor.constraint(equalTo: self.view.heightAnchor, multiplier: 0.06).isActive = true
button.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 0.7).isActive = true
button.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -20).isActive = true

Attention

order to calling the addSubview()

If you register the constraint to self.view before calling addSubview method, the app will crash.

Don't forget setting the translatesAutoresizingMaskIntoConstraints

If you forget to set the translatesAutoresizingMaskIntoConstraints to false, the constraint will not working.

Don't forget setting "isActive = true"

If you forget to set the isActive propery, the constraint will not working.

How to set the autolayout to SafeArea programatically

To set the autolayout to Safeare, you can use safeAreaLayoutGuide.

if #available(iOS 11, *) {
    button.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor, constant: -20).isActive = true
} else {
    button.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -20).isActive = true
}

How to make a margin related with other view's height by ration.

If you want to set the UIButton at the top of self.view with margin "self.view.frame.size.height * 0.3", you can use the UILayoutGuide.

In programatically autolayout setting, it not need to use UIView for spacer anymore.

let spacer = UILayoutGuide()
self.view.addLayoutGuide(spacer)
spacer.heightAnchor.constraint(equalTo: self.view.heightAnchor, multiplier: 0.5).isActive = true
spacer.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
spacer.topAnchor.constraint(equalTo: button.bottomAnchor).isActive = true

Finally

I'll show the whole code to initialize the UIButton instance and set the autolayout programatically.

lazy var mybutton: UIButton = {
    let button = UIButton()
    self.view.addSubview(button)
    button.translatesAutoresizingMaskIntoConstraints = false
    button.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
    button.heightAnchor.constraint(equalTo: self.view.heightAnchor, multiplier: 0.06).isActive = true
    button.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 0.7).isActive = true
        
    let spacer = UILayoutGuide()
    self.view.addLayoutGuide(spacer)
    spacer.heightAnchor.constraint(equalTo: self.view.heightAnchor, multiplier: 0.5).isActive = true
    spacer.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
    spacer.topAnchor.constraint(equalTo: button.bottomAnchor).isActive = true
     
    return button
} ()