MVVMCross & Xamarin.iOS 如何将菜单锚定到前一视图的按钮
iOS + MVVMCross + Xamarin 弹窗锚点传递实现方案
核心原则
不要将UI层的坐标/Frame等视图专属属性传递到ViewModel层,违反MVVM分层规则,选择ViewModel传触发源标识 + View层临时传递锚点坐标的组合方案即可。
具体实现步骤
- 步骤1:给
MenuViewModel新增可选触发源标识参数,仅用于业务层区分弹窗触发来源
public class MenuViewModel : MvxViewModel { // 示例:1代表右上角按钮触发,2代表其他按钮触发,按需扩展 public int TriggerButtonId { get; set; } // 原有业务逻辑保持不变 }
- 步骤2:在
HomeView中处理右上角按钮点击事件,同时完成导航传参与锚点暂存
// 右上角按钮点击绑定的回调方法 private void OnTopRightMenuButtonClicked(object sender, EventArgs e) { // 1. 导航到MenuViewModel,传递触发源标识 var navParams = new MvxBundle(new Dictionary<string, string> { { nameof(MenuViewModel.TriggerButtonId), "1" } }); ViewModel.ShowViewModel<MenuViewModel>(navParams); // 2. 临时存储按钮的全局坐标作为锚点,这里用静态类做轻量存储即可 PopupAnchorHelper.Shared.AnchorRect = TopRightMenuButton.Superview.ConvertRectToView(TopRightMenuButton.Bounds, null); }
- 步骤3:定义轻量锚点临时存储静态类
using CoreGraphics; public static class PopupAnchorHelper { public static CGRect AnchorRect { get; set; } = CGRect.Empty; }
- 步骤4:在
MenuView的生命周期方法中读取锚点,调整弹窗位置
如果你的弹窗是用系统UIPopoverPresentationController实现,在ViewWillAppear中设置即可:
public override void ViewWillAppear(bool animated) { base.ViewWillAppear(animated); if (PresentationController is UIPopoverPresentationController popoverController) { // 赋值之前存储的锚点 popoverController.SourceRect = PopupAnchorHelper.Shared.AnchorRect; popoverController.SourceView = View.Window; // 按需设置箭头方向 popoverController.PermittedArrowDirections = UIPopoverArrowDirection.Up; } // 用完清空锚点,避免后续复用弹窗时出现位置错误 PopupAnchorHelper.Shared.AnchorRect = CGRect.Empty; }
可选优化方案
如果不想用静态类存储锚点,可以自定义MVVMCross的iOS Presenter,在识别到要呈现MenuView时,直接从HomeView获取锚点参数传递给Presenter,在呈现逻辑中直接设置弹窗位置,耦合度会更低。
内容的提问来源于stack exchange,提问作者FinniusFrog
相关产品推荐
相关产品推荐

