開発している iOS 10 アプリをそのまま iOS 11.0 で動かすと不具合があったので修正したところです。
※ Xamarin.iOS + MvvmCross およびその他ライブラリを使っている環境です。
Document picker のデザインが異なる
iOS 11 では Document picker の navigation bar が白に、status bar の style が黒文字前提のデザインになっていました。
インポート時 (UIDocumentPickerMode.Import) の UIDocumentPickerViewController を表示したところ。
左: iOS 11 右: iOS 10
iOS 10 アプリでしていたこと
- アプリ全体の navigation bar の背景色・テキストの色を
UINavigationBar.Appearance.BarTintColor
とUINavigationBar.Appearance.TintColor
とで指定 - アプリ全体の status bar の style を info.plist で指定
<key>UIViewControllerBasedStatusBarAppearance</key> <false/> <key>UIStatusBarStyle</key> <string>UIStatusBarStyleLightContent</string>
iOS 11 アプリ用に修正したところ
- iOS 10 で行なっていたアプリ全体の navigation bar の色、status bar の style 指定をやめ、ViewController ごとに毎回指定するようにしました。
NavigationController.NavigationBar.BarTintColor = UIColor.FromRGB(0x3f, 0x51, 0xb5); NavigationController.NavigationBar.TintColor = UIColor.White; NavigationController.NavigationBar.BarStyle = UIBarStyle.Black;
修正後の画面
左: iOS 11 右: iOS 10
Document picker で選んだファイルを開けない
肝心なことが動かなくなっていました。DidPickDocument
イベントが発生せず、iOS 11 からの DidPickDocumentAtUrls
で処理する必要があるようです。
iOS 10 アプリのコード
var picker = new UIDocumentPickerViewController(new[] { "public.item" }, UIDocumentPickerMode.Import); picker.DidPickDocument += (sender, args) => { // Do something }; PresentViewController(picker, true, null);
iOS 11 アプリ用に修正したコード
var picker = new UIDocumentPickerViewController(new[] { "public.item" }, UIDocumentPickerMode.Import); if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0)) { picker.DidPickDocumentAtUrls += (sender, args) => { // Do something }; } else { picker.DidPickDocument += (sender, args) => { // Do something }; } PresentViewController(picker, true, null);
UIActivity で リソースファイルを共有できない
BundleResource としてアプリに組み込んでいるファイルを直接 BundlePath から URL 文字列を作成し UIActivityViewController へ渡すと「〜にコピー」というようなアプリへはファイルを渡せません。
iOS 10 アプリのコード
var path = Path.Combine(NSBundle.MainBundle.BundlePath, "help.pdf"); var url = new NSUrl(path, false); // filepath to url (file://) var activityItems = new NSObject[] { url }; var activityController = new UIActivityViewController(activityItems, null); if (activityController.PopoverPresentationController != null) { activityController.PopoverPresentationController.SourceView = View; activityController.PopoverPresentationController.BarButtonItem = _shareButton; } PresentViewController(activityController, true, null);
iOS 11 アプリように修正したコード
対象のファイルを一時フォルダーにコピーするようにしました。
var path = Path.Combine(NSBundle.MainBundle.BundlePath, "help.pdf"); var tempPath = Path.Combine(Path.GetTempPath(), "help.pdf"); File.Copy(path, tempPath, true); var url = new NSUrl(tempPath, false); // filepath to url (file://) var activityItems = new NSObject[] { url }; var activityController = new UIActivityViewController(activityItems, null); if (activityController.PopoverPresentationController != null) { activityController.PopoverPresentationController.SourceView = View; activityController.PopoverPresentationController.BarButtonItem = _shareButton; } PresentViewController(activityController, true, null);
UIToolbar 上の UIButton が押せない
トリッキーなコードですが、UIToobar の Subview に追加した UIButton がタップできません。
このコードがあるのが BTProgressHUD 。loading/progress 画面のキャンセルボタンが反応しません。ACR User Dialogs で参照しているライブラリなので ACR User Dialogs を使っていても問題が起こります。
※ iOS 10 では動いているので view の UserInteractionEnabled を true する話とは違います。
調べていると UIToolbar にひとつ以上 UIBarButtonItem があると UIButton も押せるという回避策を見つけました。
iOS 11 アプリでは、とりあえずライブラリのコードを修正して UIToolbar を使わず UIView に置き換えました。
MvvmCross で UITableViewCell の要素の binding で表示不具合
ニッチなのでさらっと紹介すると、MvvmCross を使って ViewModel 側でリスト項目のテキストやアイコン画像を cell の view(ImageVIew など)に binding していました。
iOS 11 では UITableView の表示と ViewModel に値を設定するタイミングで、ImageView.Image への binding している画像が表示されない現象が起きました。
View 側:
public class CustomStyleCell : MvxTableViewCell { [Export("initWithStyle:reuseIdentifier:")] public CustomStyleCell(UITableViewCellStyle style, NSString cellIdentifier) : base("", UITableViewCellStyle.Value1, cellIdentifier) { var set = this.CreateBindingSet<CustomStyleCell, ListItemViewModel>(); set.Bind(TextLabel).To(vm => vm.Text); set.Bind(DetailTextLabel).To(vm => vm.DetailText); set.Bind(ImageView).For(v => v.Image).To(vm => vm.Image).WithConversion(new NameToUIImageValueConverter()); set.Apply(); } }
public class NameToUIImageValueConverter : MvxValueConverter<string, UIImage> { protected override UIImage Convert(string value, Type targetType, object parameter, CultureInfo culture) { return (value == null) ? null : UIImage.FromBundle(value); } }
ViewModel:
public class ListItemViewModel : MvxNotifyPropertyChanged { public string Text { get => _text; set => SetProperty(ref _text, value); } private string _text; public string DetailText { get => _detailText; set => SetProperty(ref _detailText, value); } private string _detailText; public string Image { get => _image; set => SetProperty(ref _image, value); } private string _image; }
iOS 11 アプリ用に修正したコード
iOS 11 アプリでは MvxTableViewSource の GetOrCreateCellFor
内で生成した cell の ImageView.Image へ直接 ViewModel の値を設定するように「も」しました。
public class CustomTableViewSource<TCell> : MvxTableViewSource where TCell : MvxTableViewCell { protected override UITableViewCell GetOrCreateCellFor(UITableView tableView, NSIndexPath indexPath, object item) { var cell = tableView.DequeueReusableCell("cellIdentifier"); var vm = (ListItemViewModel)item; if (vm != null) { // workaround if (!string.IsNullOrWhiteSpace(vm.Image)) cell.ImageView.Image = UIImage.FromBundle(vm.Image); } return cell; } }