【问题标题】:Xamarin forms - IOS issue - Content view nested on top of each otherXamarin 表单 - IOS 问题 - 相互嵌套的内容视图
【发布时间】:2016-11-01 03:49:32
【问题描述】:

我在 Xamarin 表单中创建了一个可以翻转视图的控件(如卡片样式)。这两个视图都有某种输入(例如:按钮列表),如果您与之交互会将“卡片”控件翻转到下一个视图。我能够在 Android 上使用它,但是当我用 IOS 进行测试时,控件似乎被禁用了,我无法点击任何事件。现在我之前确实通过使用 e.NativeView.UserInteractionEnabled 属性解决了类似的问题。唯一的问题是这个属性只能在初始化视图时使用,我希望能够使用类似的更动态的东西。

IOS问题有点进展。

<?xml version="1.0" encoding="utf-8" ?>
<TemplatedView xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="minto.qm.mobile.Views.Controls.PanelView">
  <TemplatedView.ControlTemplate>
    <ControlTemplate>
      <AbsoluteLayout>
//the last item in this list will be the dominant control on runtime
//the other views input will not work 
        <ContentView Content="{TemplateBinding BackContent}" AbsoluteLayout.LayoutBounds="1,1,1,1" AbsoluteLayout.LayoutFlags="WidthProportional,HeightProportional,PositionProportional" AnchorX="0.5" AnchorY="0.5"></ContentView>
        <ContentView Content="{TemplateBinding FrontContent}" AbsoluteLayout.LayoutBounds="1,1,1,1" AbsoluteLayout.LayoutFlags="WidthProportional,HeightProportional,PositionProportional" AnchorX="0.5" AnchorY="0.5"></ContentView>
      </AbsoluteLayout>
    </ControlTemplate>
  </TemplatedView.ControlTemplate>
</TemplatedView>

有人知道这样的功能吗?

https://forums.xamarin.com/discussion/81114/how-to-create-a-custom-controls-with-multiple-view-on-top-of-each-other

更新:

这里是控件的后台代码:

public partial class PanelView : TemplatedView
    {
        public event EventHandler FrontContentAppeared;
        public event EventHandler BackContentAppeared;

        public static readonly BindableProperty FrontContentProperty = BindableProperty.Create(nameof(FrontContent), typeof(View), typeof(PanelView), defaultValue: null, propertyChanged: OnFrontContentChanged);
        public static readonly BindableProperty BackContentProperty = BindableProperty.Create(nameof(BackContent), typeof(View), typeof(PanelView), defaultValue: null, propertyChanged: OnBackContentChanged);
        public static readonly BindableProperty SwitchViewProperty = BindableProperty.Create(nameof(SwitchView), typeof(bool), typeof(PanelView), defaultValue: false, propertyChanged: OnSwitchViewChanged);

        private bool _isFrontView = true;

        public PanelView()
        {
            InitializeComponent();
        }

        private async void SwitchCurrentView()
        {
            if (_isFrontView)
            {
                BackContent.IsVisible = true;
                //BackContent.InputTransparent = true;
                FrontContent.Unfocus();
                BackContent.Focus();

                await Task.WhenAll(
                    FrontContent.FadeTo(0, 500, Easing.Linear),
                    BackContent.FadeTo(1, 500, Easing.Linear),
                    this.RotateYTo(GetRotation(0, 180), 250, Easing.Linear)
                );
                FrontContent.IsVisible = false;
                //FrontContent.InputTransparent = false;
                BackContentAppeared?.Invoke(this, EventArgs.Empty);
            }
            else
            {
                FrontContent.IsVisible = true;
                //FrontContent.InputTransparent = true;
                FrontContent.Focus();
                BackContent.Unfocus();

                await Task.WhenAll(
                    FrontContent.FadeTo(1, 500, Easing.Linear),
                    BackContent.FadeTo(0, 500, Easing.Linear),
                    this.RotateYTo(GetRotation(180, 180), 250, Easing.Linear)
                );
                BackContent.IsVisible = false;
                //BackContent.InputTransparent = false;
                FrontContentAppeared?.Invoke(this, EventArgs.Empty);
            }

            _isFrontView = !_isFrontView;
            SwitchView = false;
        }

        private static void OnFrontContentChanged(BindableObject bindable, object oldValue, object newValue)
        {
            var self = (PanelView)bindable;
            self.SetFrontContentView((View)newValue);
        }

        private static void OnBackContentChanged(BindableObject bindable, object oldValue, object newValue)
        {
            var self = (PanelView)bindable;
            self.SetBackContentView((View)newValue);
        }

        private static void OnSwitchViewChanged(BindableObject bindable, object oldValue, object newValue)
        {
            var self = (PanelView)bindable;
            self.SwitchView = (bool)newValue;
            if (self.SwitchView)
            {
                self.SwitchCurrentView();
            }
        }

        private void SetFrontContentView(View view)
        {
            FrontContent = view;
            if (!_isFrontView)
            {
                FrontContent.IsVisible = false;
                view.FadeTo(0, 1, Easing.Linear);
            }
        }

        private void SetBackContentView(View view)
        {
            view.FadeTo(0, 1, Easing.Linear);
            view.RotateYTo(180, 1, Easing.Linear);
            BackContent = view;
            if (_isFrontView)
            {
                BackContent.IsVisible = false;
            }
        }

        private double GetRotation(double start, double amount)
        {
            var rotation = (start + amount) % 360;
            return rotation;
        }

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

            if (FrontContent != null)
            {
                SetInheritedBindingContext(FrontContent, BindingContext);
            }
            if (BackContent != null)
            {
                SetInheritedBindingContext(BackContent, BindingContext);
            }
        }

        public View FrontContent
        {
            get { return (View)GetValue(FrontContentProperty); }
            set { SetValue(FrontContentProperty, value); }
        }

        public View BackContent
        {
            get { return (View)GetValue(BackContentProperty); }
            set { SetValue(BackContentProperty, value); }
        }

        public bool SwitchView
        {
            get { return (bool)GetValue(SwitchViewProperty); }
            set { SetValue(SwitchViewProperty, value); }
        }
    }

动画在可绑定的布尔变量 SwitchView 发生变化时运行

private void OnRoomTypeTapped(object sender, GliderItemTappedEventArgs e)
        {
            if (e.IsSelected && e.IsSelected == e.LastSelection)
            {
                PanelView.SwitchView = true;
            }
        }

更新:

Xaml 页面示例。注意:(Base:ExtendedPage 是自定义内容页)和(控件:Glider 是自定义视图,上面有按钮列表)

<?xml version="1.0" encoding="utf-8" ?>
<base:ExtendedPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:controls="clr-namespace:minto.qm.mobile.Views.Controls;assembly=minto.qm.mobile"
             xmlns:base="clr-namespace:minto.qm.mobile.Views.Pages.Base;assembly=minto.qm.mobile"
             x:Class="minto.qm.mobile.Views.Pages.RoomPage"
             Navigation="{Binding Navigation, Mode=OneWayToSource}"
             Title="Room">
  <base:ExtendedPage.Content>
    <ScrollView>
      <StackLayout Spacing="0">
        <ContentView BackgroundColor="#3498DB" Padding="10">
          <Label Text="{Binding SelectedRoomName}" FontAttributes="Bold" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"></Label>
        </ContentView>
        <StackLayout Spacing="5" Padding="10, 20, 10, 10">
          <controls:PanelView x:Name="PanelView">
            <controls:PanelView.FrontContent>
              <controls:Glider ItemsSource="{Binding RoomTypes}" DisplayProperty="Name" SelectedIndex="0" ItemSelected="OnRoomTypeSelected" ItemTapped="OnRoomTypeTapped" Orientation="Vertical" Lines="3" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand"></controls:Glider>
            </controls:PanelView.FrontContent>
            <controls:PanelView.BackContent>
              <controls:Glider ItemsSource="{Binding Rooms}" DisplayProperty="Name" SelectedIndex="0" ItemSelected="OnRoomSelected" ItemTapped="OnRoomTapped" Orientation="Horizontal" Lines="5" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand"></controls:Glider>
            </controls:PanelView.BackContent>
          </controls:PanelView>
          <StackLayout Orientation="Horizontal">
            <Label Text="Room annotation:" FontAttributes="Bold" Margin="5, 0, 0, 0" VerticalTextAlignment="Center"></Label>
            <Button Text="&#60;" FontAttributes="Bold" HorizontalOptions="EndAndExpand" VerticalOptions="Center" BackgroundColor="Transparent" WidthRequest="50" HeightRequest="50" Clicked="OnExpandButtonClicked" FontSize="Medium" AnchorX="0.5" AnchorY="0.5"></Button>
          </StackLayout>
          <Entry x:Name="AnnotationEditor" HorizontalOptions="FillAndExpand" BackgroundColor="#4D4D4D" TextChanged="Entry_OnTextChanged"/>
          <Button Text="Next" Command="{Binding NextPageCommand}" HorizontalOptions="FillAndExpand" />
        </StackLayout>
      </StackLayout>
    </ScrollView>
  </base:ExtendedPage.Content>
  <base:ExtendedPage.Overlay>
    <controls:Tombstone Token="{Binding DeficiencyToken}" BackgroundColor="#444444"></controls:Tombstone>
  </base:ExtendedPage.Overlay>
  <base:ExtendedPage.ToolbarItems>
    <ToolbarItem Text="Details" Order="Primary" Priority="0" Clicked="DetailsClicked"></ToolbarItem>
  </base:ExtendedPage.ToolbarItems>
</base:ExtendedPage>

public partial class RoomPage : ExtendedPage
    {
        public ViewModels.Pages.RoomPage ViewModel => _vm ?? (_vm = BindingContext as ViewModels.Pages.RoomPage);
        private ViewModels.Pages.RoomPage _vm;

        private bool _buttonExpanded;

        public RoomPage()
        {
            InitializeComponent();
            BindingContext = new ViewModels.Pages.RoomPage();
        }

        protected override void OnAppearing()
        {
            base.OnAppearing();
            ViewModel.OnAppearing();
            HandleCaching();
        }

        private async void HandleCaching()
        {
            await Task.Run(() =>
            {
                var pageCache = Services.Caches.Pages.GetInstance();
                pageCache.Preload(nameof(InspectionGalleryPage), new InspectionGalleryPage());
            });
        }

        private void DetailsClicked(object sender, EventArgs e)
        {
            ShowOverlay = !ShowOverlay;
        }

        private void Entry_OnTextChanged(object sender, TextChangedEventArgs e)
        {
            ViewModel.RoomAnnotations = e.NewTextValue;
        }

        private void OnRoomTypeSelected(object sender, GliderItemSelectedEventArgs e)
        {
            ViewModel.SelectedRoomTypeName = e.Item.ToString();
        }

        private void OnRoomTypeTapped(object sender, GliderItemTappedEventArgs e)
        {
            if (e.IsSelected && e.IsSelected == e.LastSelection)
            {
                PanelView.SwitchView = true;
            }
        }

        private void OnRoomSelected(object sender, GliderItemSelectedEventArgs e)
        {
            ViewModel.SelectedRoomName = e.Item.ToString();
        }

        private void OnRoomTapped(object sender, GliderItemTappedEventArgs e)
        {
            if (e.IsSelected && e.IsSelected == e.LastSelection)
            {
                PanelView.SwitchView = true;
            }
        }

        private void AnimateHeight(View view, double end)
        {
            view.Animate("Expander", value => view.HeightRequest = value, view.Height, end, 2, 250, Easing.Linear);
        }

        private void OnExpandButtonClicked(object sender, EventArgs e)
        {
            var button = (Button) sender;
            if (_buttonExpanded)
            {
                button.RotateTo(0, 250, Easing.Linear);
                AnimateHeight(AnnotationEditor, 45.5);
            }
            else
            {
                button.RotateTo(-90, 250, Easing.Linear);
                AnimateHeight(AnnotationEditor, 300);
            }
            _buttonExpanded = !_buttonExpanded;
        }
    }

【问题讨论】:

  • 看起来一个内容位于另一个内容之上并捕获了输入。动画结束时可以隐藏当前在后面的 ContentView 吗?如果我看到您如何进行“翻转”,我可能会提供更多帮助。如果我想重现 - TemplatedView 是从 ContentPage 派生的吗? BackContent 和 FrontContent 布局是否带有按钮?
  • @YuriS 我刚刚更新了您要求的信息。同样,两个视图都包含一个在点击事件时切换的按钮列表。
  • 上面是否为 PanelView 提供了 xaml ?我无法在屏幕上找到按钮。我看到空屏幕。看起来 Content="{TemplateBinding FrontContent}" 绑定对我不起作用。
  • @YuriS 上面的xaml是控件本身的模板。这个控件需要 2 个单独的视图(它可以有任何类型的输入),上面有一个按钮列表。如果录音它会翻转。这就是为什么您在屏幕上看到空白的原因。我可以向您展示主要的 xaml 页面示例。
  • 请提供所有必要的代码,包括带有按钮和主页的布局,这样我就不必自己编写了,我将能够快速调试它并为您提供帮助。还有一个问题,为什么你不使用这里推荐的 ControlTempate developer.xamarin.com/guides/xamarin-forms/templates/…

标签: ios xamarin.ios xamarin.forms


【解决方案1】:

正如我所说,“某物”覆盖了您的按钮。这是有问题的声明

  <AbsoluteLayout>
    <ContentView Content="{TemplateBinding BackContent}" BackgroundColor="Transparent" AbsoluteLayout.LayoutBounds="1,1,1,1" AbsoluteLayout.LayoutFlags="WidthProportional,HeightProportional,PositionProportional" AnchorX="0.5" AnchorY="0.5"></ContentView>
    <ContentView Content="{TemplateBinding FrontContent}" BackgroundColor="Transparent" AbsoluteLayout.LayoutBounds="1,1,1,1" AbsoluteLayout.LayoutFlags="WidthProportional,HeightProportional,PositionProportional" AnchorX="0.5" AnchorY="0.5"></ContentView>
  </AbsoluteLayout>

您的 FrontContent ContentView 涵盖 BackContent ContentView。当您旋转和隐藏内容时,您实际上使用的是按钮而不是视图。我发现这个设计比它应该的要复杂一些,但无论如何这不是问题。下面是解决方案。当您隐藏内容(意思是按钮)时,也会隐藏您的父视图并在显示按钮时显示它。

private async void SwitchCurrentView()
{
    if (_isFrontView)
    {
        BackContent.IsVisible = true;
       ((ContentView)BackContent.Parent).IsVisible = true;//************************

        FrontContent.Unfocus();
        BackContent.Focus();

        await Task.WhenAll(
            FrontContent.FadeTo(0, 500, Easing.Linear),
            BackContent.FadeTo(1, 500, Easing.Linear),
            this.RotateYTo(GetRotation(0, 180), 250, Easing.Linear)
        );
        FrontContent.IsVisible = false;
        ((ContentView)FrontContent.Parent).IsVisible = false;//******************

        BackContentAppeared?.Invoke(this, EventArgs.Empty);
    }
    else
    {
        FrontContent.IsVisible = true;
        ((ContentView)FrontContent.Parent).IsVisible = true;

        FrontContent.Focus();
        BackContent.Unfocus();

        await Task.WhenAll(
            FrontContent.FadeTo(1, 500, Easing.Linear),
            BackContent.FadeTo(0, 500, Easing.Linear),
            this.RotateYTo(GetRotation(180, 180), 250, Easing.Linear)
        );
        BackContent.IsVisible = false;
        ((ContentView)BackContent.Parent).IsVisible = false;

        FrontContentAppeared?.Invoke(this, EventArgs.Empty);
    }

    _isFrontView = !_isFrontView;
    SwitchView = false;
}

【讨论】:

    猜你喜欢
    • 2020-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多