英文:
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();
}
最后,要使用这个视图,可以将一个实例分配给ContentPage
的Content
属性,如下所示:
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<Page> _page;
public KeyboardAdjustingView() { }
protected override void OnParentSet()
{
base.OnParentSet();
// disconnect old Unloaded event handler, if exists
if (_page != null && _page.TryGetTarget(out Page oldPage))
{
oldPage.Unloaded -= Handle_PageUnloaded;
}
// add Unloaded event handler to parent page, so we can clean-up
if (Parent != null && TryGetPage(Parent, out Page newPage))
{
newPage.Unloaded += Handle_PageUnloaded;
_page = new WeakReference<Page>(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:
<Project Sdk="Microsoft.NET.Sdk">
...
<!-- 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>
</Project>
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<App>()
// other stuff like MauiCommunityToolkit, Essentials, Services, Fonts ...
.ConfigureMauiHandlers(handlers =>
{
// 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 = "Title";
Content = new KeyboardAdjustingView { Content = content };
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论