Yappli Tech Blog

株式会社ヤプリの開発メンバーによるブログです。最新の技術情報からチーム・働き方に関するテーマまで、日々の熱い想いを持って発信していきます。

[Swift] 親クラスのプロパティへのアクセスを禁止したい

こんにちは、ヤプリの三縞です。

iOSアプリの開発を進める中で、クラスの外からそのクラスの親のプロパティへのアクセスを禁止したい状況がありました。
今回の記事では実際の事例を紹介しながら、どのように解決したのかを説明したいと思います。

実際にあった事例

APIと通信している間の読み込み中にアニメーションを表示する、UIViewを継承したLoadingViewというクラスを作成していました。
UIViewControllerのviewに addSubview() し、APIと通信する直前に startAnimating() メソッドを呼ぶという想定です。

このLoadingViewでアニメーション表示している間はUIViewControllerの画面操作をさせたくなかったため、 startAnimating() メソッド内でUIViewのプロパティである isUserInteractionEnabledtrue にするという処理を行っています。

ところが、別の画面でLoadingViewが使用された際に、LoadingViewが表示されていても画面操作させたいという意図で以下のように isUserInteractionEnabled の値が変更されるコードが書かれました。

let loadingView = LoadingView()
view.addSubview(loadingView)
loadingView.isUserInteractionEnabled = false
loadingView.startAnimating()

startAnimating() の中で isUserInteractionEnabled = true としているため、これだと意図しないタイミングで値が変わってしまうことになります。

本来なら isUserInteractionEnabled の状態をクラス内部だけで管理したいのですが、親クラスであるUIViewのプロパティであるためprivateプロパティにすることができず外側からアクセスできてしまいます。

どうにかしてLoadingViewクラスにおける isUserInteractionEnabled プロパティをprivateであるかのような振る舞いにできないでしょうか。

やりたいこと

UIViewのプロパティである isUserInteractionEnabled をUIViewを継承したクラスの外側から操作できなくし、クラス内部でだけコントロールしたい。

実現方法

isUserInteractionEnabled をoverrideし @available (*, unavailable) を指定すればクラスの外から操作できなくなります。 プロパティをoverrideする際はsetterとgetterを実装する必要がありますが、外からも内からも使用しないので適当に実装します。

クラス内部からUIViewの isUserInteractionEnabled へアクセスしたい場合は super.isUserInteractionEnabled のように super. を付けることでアクセスできます。

最終的なコード例

class LoadingView: UIView {

    @available (*, unavailable)
    override var isUserInteractionEnabled: Bool {
        set { }
        get { return false }
    }

    override init(frame: CGRect) {
        ...
        super.isUserInteractionEnabled = false
    }

    func startAnimating() {
        super.isUserInteractionEnabled = true
        ...
    }
    ...
}

参考資料

https://stackoverflow.com/questions/36660635

環境

Xcode 13.2.1
Swift 5.4.2