Maui Handler, 在iOS的CreatePlatformView中应放置什么内容?

huangapple go评论85阅读模式
英文:

Maui Handler, Confused at what to put inside of CreatePlatformView iOS

问题

我有一个自定义视图,以前是一个网格(Grid),但我已经将它的类继承更改为视图(View),因为根据我在网上阅读的内容,这对我来说似乎是正确的做法。

  • 这个自定义视图内部有内容(一个包含自身内容的网格)。
  • 这个自定义视图在本地代码中有一个处理程序。
  • 然后每个项目都有自己的处理程序版本,我可以在其中处理映射方法。

我正在使用 ContentMapper 将自定义视图的内容添加到平台视图中(在本地 iOS 处理程序内部)。

public static void MapContent(MyHandler handler, MyView view)
{
    if (view.Content == null)
        return;
    handler.PlatformView.AddSubview(view.Content.ToPlatform(handler.MauiContext));
}

而在 CreatePlatformView()(本地 iOS 项目)内部,我目前有以下内容。

UIView uIView = new UIView();
        uIView.BackgroundColor = UIColor.Yellow;
        return uIView; 

但我看不到任何内容,但我可以看到黄色背景占据整个页面。

我已经尝试在 CreatePlatformView() 内部执行以下操作:

VirtualView.ToPlatform(VirtualView.Handler.MauiContext)

但一是它不起作用,二是我认为它本不应该起作用。

我可能做错了什么,不确定。如果需要的话,我可以创建一个演示项目并将其上传到 GitHub。

英文:

I have a custom view which previously was a Grid but I have changed its class inheritance to a View as this to me seems like the correct thing to do based on what I have read online.

  • This custom view has content inside of it. (A grid Which has content inside of itself)
  • This custom View has a handler in the native code.
  • Then each project has its own version of the handler, where I can handle the mapping methods.

I am adding the content from my custom view to the platform view using a ContentMapper (Inside the native iOS handler)

    public static void MapContent(MyHandler handler, MyView view)
    {
        if (view.Content == null)
            return;
        handler.PlatformView.AddSubview(view.Content.ToPlatform(handler.MauiContext));
    }

And inside of CreatePlatformView() (Native iOS project) I currently have.

UIView uIView = new UIView();
        uIView.BackgroundColor = UIColor.Yellow;
        return uIView; 

But I can't see any of my content, I can however see the yellow background takes up the whole page.

I Have tried doing this inside of the CreatePlatformView()
VirtualView.ToPlatform(VirtualView.Handler.MauiContext) But one it doesn't work and two I don't think that should work anyway.

I could be doing it all wrong I am unsure. If needed I can create a demo project and upload it to GitHub.

答案1

得分: 2

以下是代码的翻译部分:

我也在同样的问题上苦苦挣扎,这篇帖子没有完全解释如何解决这个问题。

在我的情况下,我想要在iOS上拥有一个`Grid`,它可以随着键盘的弹出或消失而调整大小(对于Android,这个问题可能更容易解决)。但的确,似乎更容易只是使用`ContentView`作为基类。所以我添加了一个名为`KeyboardAdjustingView`的类:

```csharp
internal class KeyboardAdjustingView : ContentView
{
    // 通过使用WeakReference来防止与包含页面的循环引用
    private WeakReference<Page> _page;

    public KeyboardAdjustingView() { }

    protected override void OnParentSet()
    {
        base.OnParentSet();

        // 断开旧的Unloaded事件处理程序,如果存在的话
        if (_page != null && _page.TryGetTarget(out Page oldPage))
        {
            oldPage.Unloaded -= Handle_PageUnloaded;
        }

        // 将Unloaded事件处理程序添加到父页面,以便进行清理
        if (Parent != null && TryGetPage(Parent, out Page newPage))
        {
            newPage.Unloaded += Handle_PageUnloaded;
            _page = new WeakReference<Page>(newPage);
        }
    }

    // 省略部分代码...
}

P.S.: 之所以使用基于ContentView的类更容易,是因为ContentView具有ContentViewHandler,但对于Grid似乎没有GridHandler

我还添加了一个部分处理程序类的实现KeyboardAdjustingViewHandler

internal partial class KeyboardAdjustingViewHandler : ContentViewHandler
{        
    public KeyboardAdjustingViewHandler() : base() { }
}

请注意,此类继承自ContentViewHandler,因为KeyboardAdjustingView类是基于ContentView的。

现在,对于iOS的实现,我们在名为KeyboardAdjustingViewHandler.iOS.cs的文件中添加部分KeyboardAdjustingViewHandler类的实现:

internal partial class KeyboardAdjustingViewHandler
{
    private NSObject _keyboardShowObserver;
    private NSObject _keyboardHideObserver;

    protected override void ConnectHandler(Microsoft.Maui.Platform.ContentView platformView)
    {
        base.ConnectHandler(platformView);
        
        RegisterForKeyboardNotifications();
    }

    protected override void DisconnectHandler(Microsoft.Maui.Platform.ContentView platformView)
    {
        UnregisterForKeyboardNotificiations();

        platformView.Dispose();            

        base.DisconnectHandler(platformView);
    }

    // 省略部分代码...
}

为了确保KeyboardAdjustingViewHandler.iOS.cs仅在iOS平台上编译,我们在项目文件(*.csproj)中使用以下指令:

<!-- Android -->
<ItemGroup Condition="$(TargetFramework.StartsWith('net7.0-android')) != true">
    <Compile Remove="**\**\*.Android.cs" />
    <None Include="**\**\*.Android.cs" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" />
</ItemGroup>

<!-- Both iOS and Mac Catalyst -->
<ItemGroup Condition="$(TargetFramework.StartsWith('net7.0-ios')) != true AND $(TargetFramework.StartsWith('net7.0-maccatalyst')) != true">
    <Compile Remove="**\**\*.MaciOS.cs" />
    <None Include="**\**\*.MaciOS.cs" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" />
</ItemGroup>

<!-- iOS -->
<ItemGroup Condition="$(TargetFramework.StartsWith('net7.0-ios')) != true">
    <Compile Remove="**\**\*.iOS.cs" />
    <None Include="**\**\*.iOS.cs" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" />
</ItemGroup>

<!-- Mac Catalyst -->
<ItemGroup Condition="$(TargetFramework.StartsWith('net7.0-maccatalyst')) != true">
    <Compile Remove="**\**\*.MacCatalyst.cs" />
</ItemGroup>

上述代码确保以iOS.cs结尾的文件仅在iOS目标上进行编译,同时也包括Android、Mac Catalyst和iOS / Mac Catalyst组合的指令。

有关更多信息,请参阅配置多目标

为了使用本机实现,在MauiProgram.cs中启动时注册我们的视图和处理程序:

public static MauiApp CreateMauiApp()
{
    var builder = MauiApp.CreateBuilder();
    builder
        .UseMauiApp<App>()
        // 其他内容,如MauiCommunityToolkit、Essentials、Services、Fonts...
        .ConfigureMauiHandlers(handlers =>
        {
            // 其他处理程序...
            handlers.AddHandler(typeof(KeyboardAdjustingView), typeof(KeyboardAdjustingViewHandler));
        })

#if DEBUG
    builder.Logging.AddDebug();
#endif

    return builder.Build();
}

最后,要使用这个视图,可以将一个实例分配给ContentPageContent属性,如下所示:

public class TextInputPage : BasePage // 从ContentPage继承
{
    public TextInputPage()
    {
        BindingContext = new TextInputViewModel();

        var content = new Grid
        {
            // 一些包含各种子Entry视图的网格...
        };

        Title = "Title";
        Content = new KeyboardAdjustingView { Content = content };
    }
}
英文:

So I was struggling on the same issue and this post didn't fully explain how to resolve the issue.

In my case I wanted to have a Grid on iOS that can resize as the keyboard pops up or disappears (for Android this issue can be resolved easier). But indeed, it seems easier to just use a ContentView as base class. So I've added a class KeyboardAdjustingView:

internal class KeyboardAdjustingView : ContentView
{
    // prevent circular references with the containing page by using a WeakReference
    private WeakReference&lt;Page&gt; _page;

    public KeyboardAdjustingView() { }

    protected override void OnParentSet()
    {
        base.OnParentSet();

        // disconnect old Unloaded event handler, if exists
        if (_page != null &amp;&amp; _page.TryGetTarget(out Page oldPage))
        {
            oldPage.Unloaded -= Handle_PageUnloaded;
        }

        // add Unloaded event handler to parent page, so we can clean-up
        if (Parent != null &amp;&amp; TryGetPage(Parent, out Page newPage))
        {
            newPage.Unloaded += Handle_PageUnloaded;
            _page = new WeakReference&lt;Page&gt;(newPage);
        }
    }

    #region Private

    // recursively find containing page
    private static bool TryGetPage(IElement element, out Page page)
    {
        page = null;

        if (element is Page pageElement)
        {
            page = pageElement;
            return true;
        }
        else
        {
            if (element.Parent != null)
            {
                return TryGetPage(element.Parent, out page);
            }
        }

        return false;
    }

    private void Handle_PageUnloaded(object sender, EventArgs e)
    {
        // no automated clean-up, so we need to do manually, see:
        // https://learn.microsoft.com/en-us/dotnet/maui/user-interface/handlers/create?view=net-maui-7.0
        Handler?.DisconnectHandler();
    }

    #endregion
}

P.S.: reason using a class based on ContentView is easier is that ContentView has a ContentViewHandler, but for Grid there doesn't seem to exist a GridHandler.

I also add a partial handler class implementation KeyboardAdjustingViewHandler:

internal partial class KeyboardAdjustingViewHandler : ContentViewHandler
{        
    public KeyboardAdjustingViewHandler() : base() { }
}

Please note this class inherits from ContentViewHandler, as the KeyboardAdjustingView class is based on ContentView.

Now for the iOS implementation we add a partial KeyboardAdjustingViewHandler class implementation in a file KeyboardAdjustingViewHandler.iOS.cs

internal partial class KeyboardAdjustingViewHandler
{
    private NSObject _keyboardShowObserver;
    private NSObject _keyboardHideObserver;

    protected override void ConnectHandler(Microsoft.Maui.Platform.ContentView platformView)
    {
        base.ConnectHandler(platformView);
        
        RegisterForKeyboardNotifications();
    }

    protected override void DisconnectHandler(Microsoft.Maui.Platform.ContentView platformView)
    {
        UnregisterForKeyboardNotificiations();

        platformView.Dispose();            

        base.DisconnectHandler(platformView);
    }

    #region Private

    private void RegisterForKeyboardNotifications()
    {
        _keyboardShowObserver = UIKeyboard.Notifications.ObserveWillShow(OnKeyboardShow);
        _keyboardHideObserver = UIKeyboard.Notifications.ObserveWillHide(OnKeyboardHide);
    }

    private void UnregisterForKeyboardNotificiations()
    {
        _keyboardShowObserver?.Dispose();
        _keyboardShowObserver = null;

        _keyboardHideObserver?.Dispose();
        _keyboardHideObserver = null;
    }

    private void OnKeyboardShow(object sender, UIKeyboardEventArgs args)
    {
        nfloat keyboardHeight = 0;

        if (args.Notification.UserInfo is NSDictionary userInfo)
        {
            var result = (NSValue)userInfo.ObjectForKey(new NSString(UIKeyboard.FrameEndUserInfoKey));
            var keyboardSize = result.RectangleFValue.Size;

            // adjust keyboard height based on safe area insets on large screen devices like iPhone X
            keyboardHeight = keyboardSize.Height - UIApplication.SharedApplication.KeyWindow.SafeAreaInsets.Bottom;
        }

        if (VirtualView is View view)
        {
            view.Margin = new Thickness(0, 0, 0, keyboardHeight);
        }
    }

    private void OnKeyboardHide(object sender, UIKeyboardEventArgs args)
    {
        if (VirtualView is View view)
        {
            view.Margin = new Thickness(0);
        }
    }

    #endregion
}

To ensure the KeyboardAdjustingViewHandler.iOS.cs is only compiled on iOS platforms we update the project file (*.csproj) with the following directives:

&lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&gt;
	...

	&lt;!-- Android --&gt;
	&lt;ItemGroup Condition=&quot;$(TargetFramework.StartsWith(&#39;net7.0-android&#39;)) != true&quot;&gt;
		&lt;Compile Remove=&quot;**\**\*.Android.cs&quot; /&gt;
		&lt;None Include=&quot;**\**\*.Android.cs&quot; Exclude=&quot;$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)&quot; /&gt;
	&lt;/ItemGroup&gt;

	&lt;!-- Both iOS and Mac Catalyst --&gt;
	&lt;ItemGroup Condition=&quot;$(TargetFramework.StartsWith(&#39;net7.0-ios&#39;)) != true AND $(TargetFramework.StartsWith(&#39;net7.0-maccatalyst&#39;)) != true&quot;&gt;
		&lt;Compile Remove=&quot;**\**\*.MaciOS.cs&quot; /&gt;
		&lt;None Include=&quot;**\**\*.MaciOS.cs&quot; Exclude=&quot;$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)&quot; /&gt;
	&lt;/ItemGroup&gt;

	&lt;!-- iOS --&gt;
	&lt;ItemGroup Condition=&quot;$(TargetFramework.StartsWith(&#39;net7.0-ios&#39;)) != true&quot;&gt;
		&lt;Compile Remove=&quot;**\**\*.iOS.cs&quot; /&gt;
		&lt;None Include=&quot;**\**\*.iOS.cs&quot; Exclude=&quot;$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)&quot; /&gt;
	&lt;/ItemGroup&gt;

	&lt;!-- Mac Catalyst --&gt;
	&lt;ItemGroup Condition=&quot;$(TargetFramework.StartsWith(&#39;net7.0-maccatalyst&#39;)) != true&quot;&gt;
		&lt;Compile Remove=&quot;**\**\*.MacCatalyst.cs&quot; /&gt;
	&lt;/ItemGroup&gt;	
&lt;/Project&gt;

The above ensures that files ending with iOS.cs are only compiled for iOS targets, but also includes directives for Android, Mac Catalyst and iOS / Mac Catalyst combined.

For more info see Configure multi-targeting

In order to use the native implementation we register our view and handler on start-up in MauiProgram.cs

public static MauiApp CreateMauiApp()
{
	var builder = MauiApp.CreateBuilder();
	builder
		.UseMauiApp&lt;App&gt;()
		// other stuff like MauiCommunityToolkit, Essentials, Services, Fonts ...
		.ConfigureMauiHandlers(handlers =&gt;
		{
			// other handlers ...
			handlers.AddHandler(typeof(KeyboardAdjustingView), typeof(KeyboardAdjustingViewHandler));
		})

#if DEBUG
    builder.Logging.AddDebug();
#endif

	return builder.Build();
}

Finally to use the view, we can assign an instance to the Content property of a ContentPage, as such:

public class TextInputPage : BasePage // inherits from ContentPage
{
	public TextInputPage()
	{
		BindingContext = new TextInputViewModel();

        var content = new Grid
        {
            // some grid with various child Entry views ...
        };

		Title = &quot;Title&quot;;
        Content = new KeyboardAdjustingView { Content = content };
	}
}

huangapple
  • 本文由 发表于 2023年2月14日 22:40:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/75449405.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定