頭と尻尾はくれてやる!

iOSアプリなどの開発日記です


マイクラのmodでScreenにスライダーを置く

Minecraftのmod(forge 1.17.1-37.1.1)でスライダーを使いたいのでvanillaのコードを参考に試してみた。
Buttonみたいにさくっと使えるとラクだけど機能が複雑なのかvanillaでは
AbstractSliderButton
を継承したクラスで処理を記述してた。

↑動作の様子はこんな感じ。
コードは以下のような感じで。CycleButtonの時と同じでScreenのサブクラス内に記述してる。
   public float mySliderValue = 0F;
private void createView() {
this.addRenderableWidget(new MySlider(x, y, w, h, mySliderValue, minValue, maxValue));
}

@OnlyIn(Dist.CLIENT)
class MySlider extends AbstractSliderButton {
private final double minValue;
private final double maxValue;

public MySlider(int x, int y, int w, int h, float initValue, float minValue, float maxValue) {
super(x, y, w, h, TextComponent.EMPTY, 0.0D);
// super(x, y, w, h, new TextComponent("text component"), 0.0D);//(A)
this.minValue = (double)minValue;
this.maxValue = (double)maxValue;
this.value = (double)((Mth.clamp(initValue, minValue, maxValue) - minValue) / (maxValue - minValue));
this.updateMessage();
}

public void applyValue() {
MyScreen.this.mySliderValue = (float)Mth.lerp(Mth.clamp(this.value, 0.0D, 1.0D), this.minValue, this.maxValue);
}

protected void updateMessage() {
System.out.println("updateMessage");
}

public void onClick(double p_89954_, double p_89955_) {
System.out.println("onClick");
}

public void onRelease(double p_89957_, double p_89958_) {
System.out.println("onRelease");
}
}
スライダーの上に数字を表示させてるが、MyScreen (Screenのサブクラス)内のrender内でdrawStringしてるだけで表示を更新してくれる。

(A)のところでsuperに渡す引数のComponentにTextComponentのオブジェクトを渡すと、

TextComponentを表示させたSlider

↑こんな感じで文字を表示する。この使い方はあまり実用性はないかもしれないが。


マイクラのCycleButton.builderの最小実装

Minecraftのmod(forge 1.17.1-37.1.1)でCycleButtonがようやく動いた。
CycleButton.onOffBuilder
CycleButton.booleanBuilder
↑この2つは取りうる値が2種類しかなくすんなりいけたのだが、選択肢が3つ以上ある場合にハマった。
もっともJavaに慣れてる人なら簡単だったのかもしれないけど。
Screenのサブクラス内のコードは以下のような感じで。
    private MyMode myMode = MyMode.MODE1;

private void createView() {
this.addRenderableWidget(CycleButton.builder(MyMode::getCurrentComponent)
.withValues(MyMode.values())
.withInitialValue(myMode)
.create(x, y, width, height, new TextComponent("Mode"), (p_169299_, p_169300_) -> {
this.myMode = p_169300_;
}));
}

public static enum MyMode implements StringRepresentable {
MODE1("mode 1"),
MODE2("mode 2"),
MODE3("mode 3");

private final String name;
private MyMode(String p_59455_) {
this.name = p_59455_;
}

@Override
public String getSerializedName() {
return this.name;
}

public TextComponent getCurrentComponent() {
return new TextComponent(this.name);
}
}

CycleButton
↑スクショ。クリックするたびに表示内容が変わります(これは画像なので変わらないけど)。
なお、1.18だとどうなのかはわかりません。1.18にアップデートしたら挙動がおかしくなり結局1.17に戻していじっています、、、orz


PHPickerViewControllerで動画ファイルを選択した後、Task内だと動かない

現在開発してるiOSアプリで、iPhoneのライブラリから動画ファイルを選択する処理を作ってた。
最近PHPickerControllerというのがあるみたいで試してたんだが、ユーザーに許可を得なくてもいいらしく、こりゃいいや!と試してたんだが、、、
PHPickerViewControllerDelegateのメソッドである
func picker(PHPickerViewController, didFinishPicking: [PHPickerResult])
でurl: URLを取得まではOK。その後続けて処理をしていくんだが、例えば動画が記録された日付を得るとする。

//上記メソッド内
let asset = AVURLAsset(url: url)
guard let creationDate = asset.creationDate?.value as? Date else { abort() }
print(creationDate)

↑これは意図通りに動く。
問題はasync/awaitを使うために以下のように書き換えた場合だ。

Task {
let asset = AVURLAsset(url: url)
guard let creationDate = asset.creationDate?.value as? Date else { abort() }
print(creationDate)
}

↑これだとabortで処理が止まる。urlは有効で、assetは一応できてるっぽい。
Task内の処理にtry await someFunction(…とかあるんだがそこまで辿り着かない。orz

困ったなあ、、、
試しにUIImagePickerController(PHPickerControllerが出る前はこれを使ってたよね)でやってみるとこれが動いたんだわ。
でもUIImagePickerControllerのUIってあまり好きじゃないしなあ。
ユーザーに許可を求める必要あるし(できればない方がいい)。
せっかく苦労してasync/await実装できたのに、元に戻してcompletionHandler使うか?うーん、、、

と思ってたところ試しに得たファイルをdocumentDirectoryにコピーしてから、それに対して処理をするようにしたらasync/await使ってても動いた!

めでたし、めでたし。

おおよそのコードはこんな感じ。
    @IBAction func tappedSelectVideoButton() {

var configuration = PHPickerConfiguration()
configuration.selectionLimit = 1//default
configuration.filter = .videos
configuration.preferredAssetRepresentationMode = .current

let pickerViewController = PHPickerViewController(configuration: configuration)
pickerViewController.delegate = self
self.present(pickerViewController, animated: true)
}

func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {

picker.dismiss(animated: true)

guard let provider = results.first?.itemProvider else { return }
guard let typeIdentifier = provider.registeredTypeIdentifiers.first else { return }

if provider.hasItemConformingToTypeIdentifier(typeIdentifier) {
provider.loadFileRepresentation(forTypeIdentifier: typeIdentifier) { [weak self] (url, error) in
if let url = url {
let copiedFileURL = self?.copyFile(url)//詳細略
assert(copiedFileURL != nil, "copiedFileURL is nil")

Task {
do {
let r = try await analyzer.start(copiedFileURL!)
print(r)
} catch {
//以下略




iOSアプリが「バイナリが無効」でApp Store Connectにuploadできない

わざタイプ相性表 for ポケモンGO
↑このアプリのアップデートを申請しようとした時の話。初めて見るケースなのでメモ。


修正版(ver1.2)をXcodeでarchiveしてvalidation通した後にuploadした(Xcode上ではuploadはできてるように表示される)。
通常ならApp Store Connectでそのファイルが見えて「処理中」とかになってたりするのだが、、、

App Store Connectアプリの通知

↑なぜかiPhoneのApp Store Connectアプリから
ビルドステータスが”バイナリが無効”に変更されました
という通知が来た。
App Store Connectのページにはいくら待っても修正版のファイルは表示されない。

App Store Connect側の問題かなあ?よくあるよなあ?と思って同じファイルを再度uploadしようとしたが、

警告内容

↑Xcodeが「そのバージョンすでにあるから変えて」と。
なのでver1.2.1でarchiveしたのをuploadしたけど同じくバイナリが無効、でuploadできなかった。

ここでネットで調べたらメールに情報が記載されてるというのを見て、そうだっけ?と思いメールを確認すると、確かに詳細が書かれたメールが届いてた。

ITMS-90713: Missing Info.plist value - A value for the Info.plist key 'CFBundleIconName' is missing in the bundle ‘xxx.Clip'. Apps built with iOS 11 or later SDK must supply app icons in an asset catalog and must also provide a value for this Info.plist key. For more information see http://(略)

App Clip側のCFBundleIconNameがない、、、?
ちなみにこのアプリ、App Clipが登場した時に試してみよう!と思って勢いでApp Clipを実装したんだわ。そのアイコンがないってこと、、、?このアプリ今までに何回もアップデートしてたのに何を今更、、、?そもそもApp Clipにアイコンなんて必要なんだっけ?
と思いつつとりあえずApp Clipのアセットにアイコン画像を置いた(メインのアセットと同じ画像ファイルを並べただけだ)。

この状態でarchive, validation通してuploadすると、、、無事申請できた!

ところが、だ。その日の夜にリジェクトされた。

ガイドライン2.5.16違反。内容はApp Clipがemptyとか。
え?アイコンだけじゃなくてすっからかんだと?何も変えてないぞ?

targetの設定には問題なさそうだし、、、archive時のschemeの設定がおかしくなってるのか、、、?
さらに調べるのも面倒だし、、、App Clipなんて誰も使わないし、、、

App Clipを消そう!

ということでXcodeでApp Clip用のtargetを削除、関連のschemeも削除。entitlementsのファイル内にApp Clipに関連する記述があったので削除。
そして再度archive, validation, upload。申請したらその日の夜に審査をパスしてた。


メールに必要な情報が書かれてることがあるってのは覚えておかねば、、、でも内心そんな重大な問題はarchive時かvalidationで教えてくれよ!とも思う。



Xcodeで画像ファイルの削除ができない?!

あるXcodeのプロジェクト内にある画像ファイル(なのでAssets.xcassetsにある)を削除しようとすると
警告:The file doesn’t exist
The file xxx doesn’t exist.
と表示されて消しても消しても元に戻ってしまう、という不可解な現象が発生した。
プロジェクトを再loadしたり、Xcodeを再起動したり、Macを再起動したりdeprived dataのファイルを消したりしてもダメ、、、

Finderでその画像ファイルあたりを見るとContents.jsonってファイルがあってどうもXcodeはこれと整合性がとれないと嫌がるようなので、このjsonファイルを自分で修正したらXcodeの方もおとなしくなった。
でもまあ多分、ウチのXcodeがおかしいんだろうな、、、




Copyright ©頭と尻尾はくれてやる!. Powered by FC2 Blog. Template by eriraha.