[NUI] Separate the LayoutTransition from LayoutController (#3592)
authorhuiyu <35286162+huiyueun@users.noreply.github.com>
Fri, 17 Sep 2021 08:14:46 +0000 (17:14 +0900)
committerdongsug-song <35130733+dongsug-song@users.noreply.github.com>
Mon, 27 Sep 2021 08:27:23 +0000 (17:27 +0900)
Separate class to help understanding for layout process.

Signed-off-by: huiyu.eun <huiyu.eun@samsung.com>
src/Tizen.NUI/src/internal/Layouting/LayoutController.cs
src/Tizen.NUI/src/internal/Layouting/LayoutTransitionManager.cs [new file with mode: 0755]

index b6574c7..adb9e41 100755 (executable)
@@ -16,9 +16,8 @@
  */
 
 using Tizen.NUI.BaseComponents;
-using System.Collections.Generic;
-using System;
 using System.ComponentModel;
+using System;
 
 namespace Tizen.NUI
 {
@@ -32,11 +31,9 @@ namespace Tizen.NUI
 
         private int id;
         private Window window;
-        private Animation coreAnimation;
-        private List<LayoutData> layoutTransitionDataQueue;
-        private List<LayoutItem> itemRemovalQueue;
         private float windowWidth;
         private float windowHeight;
+        private LayoutTransitionManager transitionManager;
 
         private bool subscribed;
 
@@ -46,23 +43,40 @@ namespace Tizen.NUI
         /// </summary>
         public LayoutController(Window window)
         {
-            this.window = window;
-            this.window.Resized += OnWindowResized;
-            var windowSize = window.GetSize();
-            windowWidth = windowSize.Width;
-            windowHeight = windowSize.Height;
-
-            layoutTransitionDataQueue = new List<LayoutData>();
+            transitionManager = new LayoutTransitionManager();
             id = layoutControllerID++;
 
-            windowSize.Dispose();
-            windowSize = null;
+            SetBaseWindowAndSize(window);
         }
 
-        private void OnWindowResized(object sender, Window.ResizedEventArgs e)
+        /// <summary>
+        /// Dispose Explicit or Implicit
+        /// </summary>
+        protected override void Dispose(DisposeTypes type)
         {
-            windowWidth = e.WindowSize.Width;
-            windowHeight = e.WindowSize.Height;
+            if (Disposed)
+            {
+                return;
+            }
+
+            if (subscribed)
+            {
+                ProcessorController.Instance.LayoutProcessorEvent -= Process;
+                subscribed = false;
+            }
+
+            //Release your own unmanaged resources here.
+            //You should not access any managed member here except static instance.
+            //because the execution order of Finalizes is non-deterministic.
+
+            if (SwigCPtr.Handle != global::System.IntPtr.Zero)
+            {
+                SwigCPtr = new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero);
+            }
+
+            transitionManager.Dispose();
+
+            base.Dispose(type);
         }
 
         /// <summary>
@@ -71,7 +85,17 @@ namespace Tizen.NUI
         /// Reset to False if explicit control no longer required.
         /// </summary>
         [EditorBrowsable(EditorBrowsableState.Never)]
-        public bool OverrideCoreAnimation { get; set; } = false;
+        public bool OverrideCoreAnimation
+        {
+            get
+            {
+                return transitionManager.OverrideCoreAnimation;
+            }
+            set
+            {
+                transitionManager.OverrideCoreAnimation = value;
+            }
+        }
 
         /// <summary>
         /// Get the unique id of the LayoutController
@@ -99,7 +123,6 @@ namespace Tizen.NUI
             }
         }
 
-
         /// <summary>
         /// Entry point into the C# Layouting that starts the Processing
         /// </summary>
@@ -116,11 +139,9 @@ namespace Tizen.NUI
                 });
             });
 
-            if (SetupCoreAnimation() && OverrideCoreAnimation == false)
-            {
-                PlayAnimation();
-            }
+            transitionManager.SetupCoreAndPlayAnimation();
         }
+
         /// <summary>
         /// Get the Layouting animation object that transitions layouts and content.
         /// Use OverrideCoreAnimation to explicitly control Playback.
@@ -129,7 +150,7 @@ namespace Tizen.NUI
         [EditorBrowsable(EditorBrowsableState.Never)]
         public Animation GetCoreAnimation()
         {
-            return coreAnimation;
+            return transitionManager.CoreAnimation;
         }
 
         /// <summary>
@@ -150,7 +171,7 @@ namespace Tizen.NUI
         /// <param name="transitionDataEntry">Transition data for a LayoutItem.</param>
         internal void AddTransitionDataEntry(LayoutData transitionDataEntry)
         {
-            layoutTransitionDataQueue.Add(transitionDataEntry);
+            transitionManager.AddTransitionDataEntry(transitionDataEntry);
         }
 
         /// <summary>
@@ -159,39 +180,7 @@ namespace Tizen.NUI
         /// <param name="itemToRemove">LayoutItem to remove.</param>
         internal void AddToRemovalStack(LayoutItem itemToRemove)
         {
-            if (itemRemovalQueue == null)
-            {
-                itemRemovalQueue = new List<LayoutItem>();
-            }
-            itemRemovalQueue.Add(itemToRemove);
-        }
-
-        /// <summary>
-        /// Dispose Explicit or Implicit
-        /// </summary>
-        protected override void Dispose(DisposeTypes type)
-        {
-            if (disposed)
-            {
-                return;
-            }
-
-            if (subscribed)
-            {
-                ProcessorController.Instance.LayoutProcessorEvent -= Process;
-                subscribed = false;
-            }
-
-            //Release your own unmanaged resources here.
-            //You should not access any managed member here except static instance.
-            //because the execution order of Finalizes is non-deterministic.
-
-            if (SwigCPtr.Handle != global::System.IntPtr.Zero)
-            {
-                SwigCPtr = new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero);
-            }
-
-            base.Dispose(type);
+            transitionManager.AddToRemovalStack(itemToRemove);
         }
 
         // Traverse the tree looking for a root node that is a layout.
@@ -294,266 +283,22 @@ namespace Tizen.NUI
             root.Layout?.Layout(left, top, right, bottom);
         }
 
-        /// <summary>
-        /// Play the animation.
-        /// </summary>
-        private void PlayAnimation()
-        {
-            NUILog.Debug("LayoutController Playing, Core Duration:" + coreAnimation.Duration);
-            coreAnimation.Play();
-        }
-
-        private void AnimationFinished(object sender, EventArgs e)
-        {
-            // Iterate list of LayoutItem that were set for removal.
-            // Now the core animation has finished their Views can be removed.
-            if (itemRemovalQueue != null)
-            {
-                foreach (LayoutItem item in itemRemovalQueue)
-                {
-                    // Check incase the parent was already removed and the Owner was
-                    // removed already.
-                    if (item.Owner)
-                    {
-                        // Check again incase the parent has already been removed.
-                        ILayoutParent layoutParent = item.GetParent();
-                        LayoutGroup layoutGroup = layoutParent as LayoutGroup;
-                        if (layoutGroup != null)
-                        {
-                            layoutGroup.Owner?.RemoveChild(item.Owner);
-                        }
-
-                    }
-                }
-                itemRemovalQueue.Clear();
-                // If LayoutItems added to stack whilst the core animation is playing
-                // they would have been cleared here.
-                // Could have another stack to be added to whilst the animation is running.
-                // After the running stack is cleared it can be populated with the content
-                // of the other stack.  Then the main removal stack iterated when AnimationFinished
-                // occurs again.
-            }
-            NUILog.Debug("LayoutController AnimationFinished");
-            coreAnimation?.Clear();
-        }
-
-        /// <summary>
-        /// Set up the animation from each LayoutItems position data.
-        /// Iterates the transition stack, adding an Animator to the core animation.
-        /// </summary>
-        private bool SetupCoreAnimation()
+        private void SetBaseWindowAndSize(Window window)
         {
-            // Initialize animation for this layout run.
-            bool animationPending = false;
-
-            NUILog.Debug("LayoutController SetupCoreAnimation for:" + layoutTransitionDataQueue.Count);
-
-            if (layoutTransitionDataQueue.Count > 0) // Something to animate
-            {
-                if (!coreAnimation)
-                {
-                    coreAnimation = new Animation();
-                }
-                coreAnimation.EndAction = Animation.EndActions.StopFinal;
-                coreAnimation.Finished += AnimationFinished;
-
-                // Iterate all items that have been queued for repositioning.
-                foreach (LayoutData layoutPositionData in layoutTransitionDataQueue)
-                {
-                    AddAnimatorsToAnimation(layoutPositionData);
-                }
-
-                animationPending = true;
-
-                // transitions have now been applied, clear stack, ready for new transitions on
-                // next layout traversal.
-                layoutTransitionDataQueue.Clear();
-            }
-            return animationPending;
-        }
-
-        private void SetupAnimationForPosition(LayoutData layoutPositionData, TransitionComponents positionTransitionComponents)
-        {
-            // A removed item does not have a valid target position within the layout so don't try to position.
-            if (layoutPositionData.ConditionForAnimation != TransitionCondition.Remove)
-            {
-                coreAnimation.AnimateTo(layoutPositionData.Item.Owner, "Position",
-                            new Vector3(layoutPositionData.Left,
-                                        layoutPositionData.Top,
-                                        layoutPositionData.Item.Owner.Position.Z),
-                            positionTransitionComponents.Delay,
-                            positionTransitionComponents.Duration,
-                            positionTransitionComponents.AlphaFunction);
-
-                NUILog.Debug("LayoutController SetupAnimationForPosition View:" + layoutPositionData.Item.Owner.Name +
-                                   " left:" + layoutPositionData.Left +
-                                   " top:" + layoutPositionData.Top +
-                                   " delay:" + positionTransitionComponents.Delay +
-                                   " duration:" + positionTransitionComponents.Duration);
-            }
-        }
-
-        private void SetupAnimationForSize(LayoutData layoutPositionData, TransitionComponents sizeTransitionComponents)
-        {
-            // Text size cant be animated so is set to it's final size.
-            // It is due to the internals of the Text not being able to recalculate fast enough.
-            if (layoutPositionData.Item.Owner is TextLabel || layoutPositionData.Item.Owner is TextField)
-            {
-                float itemWidth = layoutPositionData.Right - layoutPositionData.Left;
-                float itemHeight = layoutPositionData.Bottom - layoutPositionData.Top;
-                // Set size directly.
-                layoutPositionData.Item.Owner.Size2D = new Size2D((int)itemWidth, (int)itemHeight);
-            }
-            else
-            {
-                coreAnimation.AnimateTo(layoutPositionData.Item.Owner, "Size",
-                                         new Vector3(layoutPositionData.Right - layoutPositionData.Left,
-                                                     layoutPositionData.Bottom - layoutPositionData.Top,
-                                                     layoutPositionData.Item.Owner.Position.Z),
-                                         sizeTransitionComponents.Delay,
-                                         sizeTransitionComponents.Duration,
-                                         sizeTransitionComponents.AlphaFunction);
-
-                NUILog.Debug("LayoutController SetupAnimationForSize View:" + layoutPositionData.Item.Owner.Name +
-                                   " width:" + (layoutPositionData.Right - layoutPositionData.Left) +
-                                   " height:" + (layoutPositionData.Bottom - layoutPositionData.Top) +
-                                   " delay:" + sizeTransitionComponents.Delay +
-                                   " duration:" + sizeTransitionComponents.Duration);
-            }
-        }
-
-        void SetupAnimationForCustomTransitions(TransitionList transitionsToAnimate, View view)
-        {
-            if (transitionsToAnimate?.Count > 0)
-            {
-                foreach (LayoutTransition transition in transitionsToAnimate)
-                {
-                    if (transition.AnimatableProperty != AnimatableProperties.Position &&
-                        transition.AnimatableProperty != AnimatableProperties.Size)
-                    {
-                        coreAnimation.AnimateTo(view,
-                                                 transition.AnimatableProperty.ToString(),
-                                                 transition.TargetValue,
-                                                 transition.Animator.Delay,
-                                                 transition.Animator.Duration,
-                                                 transition.Animator.AlphaFunction);
-
-                        NUILog.Debug("LayoutController SetupAnimationForCustomTransitions View:" + view.Name +
-                                           " Property:" + transition.AnimatableProperty.ToString() +
-                                           " delay:" + transition.Animator.Delay +
-                                           " duration:" + transition.Animator.Duration);
-                    }
-                }
-            }
-        }
-
-        /// <summary>
-        /// Iterate transitions and replace Position Components if replacements found in list.
-        /// </summary>
-        private void FindAndReplaceAnimatorComponentsForProperty(TransitionList sourceTransitionList,
-                                                                 AnimatableProperties propertyToMatch,
-                                                                 ref TransitionComponents transitionComponentToUpdate)
-        {
-            foreach (LayoutTransition transition in sourceTransitionList)
-            {
-                if (transition.AnimatableProperty == propertyToMatch)
-                {
-                    // Matched property to animate is for the propertyToMatch so use provided Animator.
-                    transitionComponentToUpdate = transition.Animator;
-                }
-            }
-        }
+            this.window = window;
+            this.window.Resized += OnWindowResized;
 
-        private TransitionComponents CreateDefaultTransitionComponent(int delay, int duration)
-        {
-            AlphaFunction alphaFunction = new AlphaFunction(AlphaFunction.BuiltinFunctions.Linear);
-            return new TransitionComponents(delay, duration, alphaFunction);
+            var windowSize = window.GetSize();
+            windowWidth = windowSize.Width;
+            windowHeight = windowSize.Height;
+            windowSize.Dispose();
+            windowSize = null;
         }
 
-        /// <summary>
-        /// Sets up the main animation with the animators for each item (each layoutPositionData structure)
-        /// </summary>
-        private void AddAnimatorsToAnimation(LayoutData layoutPositionData)
+        private void OnWindowResized(object sender, Window.ResizedEventArgs e)
         {
-            LayoutTransition positionTransition = new LayoutTransition();
-            LayoutTransition sizeTransition = new LayoutTransition();
-            TransitionCondition conditionForAnimators = layoutPositionData.ConditionForAnimation;
-
-            // LayoutChanged transitions overrides ChangeOnAdd and ChangeOnRemove as siblings will
-            // reposition to the new layout not to the insertion/removal of a sibling.
-            if (layoutPositionData.ConditionForAnimation.HasFlag(TransitionCondition.LayoutChanged))
-            {
-                conditionForAnimators = TransitionCondition.LayoutChanged;
-            }
-
-            // Set up a default transitions, will be overwritten if inherited from parent or set explicitly.
-            TransitionComponents positionTransitionComponents = CreateDefaultTransitionComponent(0, 300);
-            TransitionComponents sizeTransitionComponents = CreateDefaultTransitionComponent(0, 300);
-
-            bool matchedCustomTransitions = false;
-
-
-            TransitionList transitionsForCurrentCondition = new TransitionList();
-            // Note, Transitions set on View rather than LayoutItem so if the Layout changes the transition persist.
-
-            // Check if item to animate has it's own Transitions for this condition.
-            // If a key exists then a List of at least 1 transition exists.
-            if (layoutPositionData.Item.Owner.LayoutTransitions.ContainsKey(conditionForAnimators))
-            {
-                // Child has transitions for the condition
-                matchedCustomTransitions = layoutPositionData.Item.Owner.LayoutTransitions.TryGetValue(conditionForAnimators, out transitionsForCurrentCondition);
-            }
-
-            if (!matchedCustomTransitions)
-            {
-                // Inherit parent transitions as none already set on View for the condition.
-                ILayoutParent layoutParent = layoutPositionData.Item.GetParent();
-                if (layoutParent != null)
-                {
-                    // Item doesn't have it's own transitions for this condition so copy parents if
-                    // has a parent with transitions.
-                    LayoutGroup layoutGroup = layoutParent as LayoutGroup;
-                    TransitionList parentTransitionList;
-                    // Note TryGetValue returns null if key not matched.
-                    if (layoutGroup != null && layoutGroup.Owner.LayoutTransitions.TryGetValue(conditionForAnimators, out parentTransitionList))
-                    {
-                        // Copy parent transitions to temporary TransitionList. List contains transitions for the current condition.
-                        LayoutTransitionsHelper.CopyTransitions(parentTransitionList,
-                                                                transitionsForCurrentCondition);
-                    }
-                }
-            }
-
-
-            // Position/Size transitions can be displayed for a layout changing to another layout or an item being added or removed.
-
-            // There can only be one position transition and one size position, they will be replaced if set multiple times.
-            // transitionsForCurrentCondition represent all non position (custom) properties that should be animated.
-
-            // Search for Position property in the transitionsForCurrentCondition list of custom transitions,
-            // and only use the particular parts of the animator as custom transitions should not effect all parameters of Position.
-            // Typically Delay, Duration and Alphafunction can be custom.
-            FindAndReplaceAnimatorComponentsForProperty(transitionsForCurrentCondition,
-                                                        AnimatableProperties.Position,
-                                                        ref positionTransitionComponents);
-
-            // Size
-            FindAndReplaceAnimatorComponentsForProperty(transitionsForCurrentCondition,
-                                                        AnimatableProperties.Size,
-                                                        ref sizeTransitionComponents);
-
-            // Add animators to the core Animation,
-
-            SetupAnimationForCustomTransitions(transitionsForCurrentCondition, layoutPositionData.Item.Owner);
-
-            SetupAnimationForPosition(layoutPositionData, positionTransitionComponents);
-
-            SetupAnimationForSize(layoutPositionData, sizeTransitionComponents);
-
-            // Dispose components
-            positionTransitionComponents.Dispose();
-            sizeTransitionComponents.Dispose();
+            windowWidth = e.WindowSize.Width;
+            windowHeight = e.WindowSize.Height;
         }
-
     } // class LayoutController
 } // namespace Tizen.NUI
diff --git a/src/Tizen.NUI/src/internal/Layouting/LayoutTransitionManager.cs b/src/Tizen.NUI/src/internal/Layouting/LayoutTransitionManager.cs
new file mode 100755 (executable)
index 0000000..803a8bc
--- /dev/null
@@ -0,0 +1,361 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+using System.Collections.Generic;
+using Tizen.NUI.BaseComponents;
+
+namespace Tizen.NUI
+{
+    internal class LayoutTransitionManager : Disposable
+    {
+        private bool overrideCoreAnimation = false;
+        private Animation coreAnimation;
+        private List<LayoutData> layoutTransitionDataQueue;
+        private List<LayoutItem> itemRemovalQueue;
+
+        internal LayoutTransitionManager()
+        {
+            layoutTransitionDataQueue = new List<LayoutData>();
+
+        }
+
+        /// <summary>
+        /// Dispose Explicit or Implicit
+        /// </summary>
+        protected override void Dispose(DisposeTypes type)
+        {
+            if (Disposed)
+            {
+                return;
+            }
+
+            if (coreAnimation != null)
+            {
+                coreAnimation.Dispose();
+                coreAnimation = null;
+            }
+
+            base.Dispose(type);
+        }
+
+        public Animation CoreAnimation => coreAnimation;
+
+        public bool OverrideCoreAnimation
+        {
+            get
+            {
+                return overrideCoreAnimation;
+            }
+            set
+            {
+                overrideCoreAnimation = value;
+            }
+        }
+
+        /// <summary>
+        /// Add transition data for a LayoutItem to the transition stack.
+        /// </summary>
+        /// <param name="transitionDataEntry">Transition data for a LayoutItem.</param>
+        internal void AddTransitionDataEntry(LayoutData transitionDataEntry)
+        {
+            layoutTransitionDataQueue.Add(transitionDataEntry);
+        }
+
+        /// <summary>
+        /// Add LayoutItem to a removal stack for removal after transitions finish.
+        /// </summary>
+        /// <param name="itemToRemove">LayoutItem to remove.</param>
+        internal void AddToRemovalStack(LayoutItem itemToRemove)
+        {
+            if (itemRemovalQueue == null)
+            {
+                itemRemovalQueue = new List<LayoutItem>();
+            }
+            itemRemovalQueue.Add(itemToRemove);
+        }
+
+        /// <summary>
+        /// Play the animation.
+        /// </summary>
+        internal void SetupCoreAndPlayAnimation()
+        {
+            if (EnabledCoreAnimation())
+            {
+                SetupCoreAnimation();
+
+                NUILog.Debug("LayoutController Playing, Core Duration:" + coreAnimation.Duration);
+                coreAnimation.Play();
+            }
+        }
+
+        /// <summary>
+        /// Check if layout animation is needed
+        /// </summary>
+        private bool EnabledCoreAnimation()
+        {
+            return layoutTransitionDataQueue.Count > 0 && !OverrideCoreAnimation;
+        }
+
+        /// <summary>
+        /// Set up the animation from each LayoutItems position data.
+        /// Iterates the transition stack, adding an Animator to the core animation.
+        /// </summary>
+        private void SetupCoreAnimation()
+        {
+            NUILog.Debug("LayoutController SetupCoreAnimation for:" + layoutTransitionDataQueue.Count);
+
+            if (!coreAnimation)
+            {
+                coreAnimation = new Animation();
+                coreAnimation.EndAction = Animation.EndActions.StopFinal;
+                coreAnimation.Finished += AnimationFinished;
+            }
+
+            // Iterate all items that have been queued for repositioning.
+            foreach (var layoutPositionData in layoutTransitionDataQueue)
+            {
+                AddAnimatorsToAnimation(layoutPositionData);
+            }
+            // transitions have now been applied, clear stack, ready for new transitions on
+            // next layout traversal.
+            layoutTransitionDataQueue.Clear();
+        }
+
+
+        private void SetupAnimationForCustomTransitions(TransitionList transitionsToAnimate, View view)
+        {
+            if (transitionsToAnimate?.Count > 0)
+            {
+                foreach (LayoutTransition transition in transitionsToAnimate)
+                {
+                    if (transition.AnimatableProperty != AnimatableProperties.Position &&
+                        transition.AnimatableProperty != AnimatableProperties.Size)
+                    {
+                        coreAnimation.AnimateTo(view,
+                                                 transition.AnimatableProperty.ToString(),
+                                                 transition.TargetValue,
+                                                 transition.Animator.Delay,
+                                                 transition.Animator.Duration,
+                                                 transition.Animator.AlphaFunction);
+
+                        NUILog.Debug("LayoutController SetupAnimationForCustomTransitions View:" + view.Name +
+                                           " Property:" + transition.AnimatableProperty.ToString() +
+                                           " delay:" + transition.Animator.Delay +
+                                           " duration:" + transition.Animator.Duration);
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// Iterate transitions and replace Position Components if replacements found in list.
+        /// </summary>
+        private void FindAndReplaceAnimatorComponentsForProperty(TransitionList sourceTransitionList,
+                                                                 AnimatableProperties propertyToMatch,
+                                                                 ref TransitionComponents transitionComponentToUpdate)
+        {
+            foreach (LayoutTransition transition in sourceTransitionList)
+            {
+                if (transition.AnimatableProperty == propertyToMatch)
+                {
+                    // Matched property to animate is for the propertyToMatch so use provided Animator.
+                    transitionComponentToUpdate = transition.Animator;
+                }
+            }
+        }
+
+        private TransitionComponents CreateDefaultTransitionComponent(int delay, int duration)
+        {
+            AlphaFunction alphaFunction = new AlphaFunction(AlphaFunction.BuiltinFunctions.Linear);
+            return new TransitionComponents(delay, duration, alphaFunction);
+        }
+
+        /// <summary>
+        /// Sets up the main animation with the animators for each item (each layoutPositionData structure)
+        /// </summary>
+        private void AddAnimatorsToAnimation(LayoutData layoutPositionData)
+        {
+            LayoutTransition positionTransition = new LayoutTransition();
+            LayoutTransition sizeTransition = new LayoutTransition();
+            TransitionCondition conditionForAnimators = layoutPositionData.ConditionForAnimation;
+
+            // LayoutChanged transitions overrides ChangeOnAdd and ChangeOnRemove as siblings will
+            // reposition to the new layout not to the insertion/removal of a sibling.
+            if (layoutPositionData.ConditionForAnimation.HasFlag(TransitionCondition.LayoutChanged))
+            {
+                conditionForAnimators = TransitionCondition.LayoutChanged;
+            }
+
+            // Set up a default transitions, will be overwritten if inherited from parent or set explicitly.
+            TransitionComponents positionTransitionComponents = CreateDefaultTransitionComponent(0, 300);
+            TransitionComponents sizeTransitionComponents = CreateDefaultTransitionComponent(0, 300);
+
+            bool matchedCustomTransitions = false;
+
+
+            TransitionList transitionsForCurrentCondition = new TransitionList();
+            // Note, Transitions set on View rather than LayoutItem so if the Layout changes the transition persist.
+
+            // Check if item to animate has it's own Transitions for this condition.
+            // If a key exists then a List of at least 1 transition exists.
+            if (layoutPositionData.Item.Owner.LayoutTransitions.ContainsKey(conditionForAnimators))
+            {
+                // Child has transitions for the condition
+                matchedCustomTransitions = layoutPositionData.Item.Owner.LayoutTransitions.TryGetValue(conditionForAnimators, out transitionsForCurrentCondition);
+            }
+
+            if (!matchedCustomTransitions)
+            {
+                // Inherit parent transitions as none already set on View for the condition.
+                ILayoutParent layoutParent = layoutPositionData.Item.GetParent();
+                if (layoutParent != null)
+                {
+                    // Item doesn't have it's own transitions for this condition so copy parents if
+                    // has a parent with transitions.
+                    LayoutGroup layoutGroup = layoutParent as LayoutGroup;
+                    TransitionList parentTransitionList;
+                    // Note TryGetValue returns null if key not matched.
+                    if (layoutGroup != null && layoutGroup.Owner.LayoutTransitions.TryGetValue(conditionForAnimators, out parentTransitionList))
+                    {
+                        // Copy parent transitions to temporary TransitionList. List contains transitions for the current condition.
+                        LayoutTransitionsHelper.CopyTransitions(parentTransitionList,
+                                                                transitionsForCurrentCondition);
+                    }
+                }
+            }
+
+
+            // Position/Size transitions can be displayed for a layout changing to another layout or an item being added or removed.
+
+            // There can only be one position transition and one size position, they will be replaced if set multiple times.
+            // transitionsForCurrentCondition represent all non position (custom) properties that should be animated.
+
+            // Search for Position property in the transitionsForCurrentCondition list of custom transitions,
+            // and only use the particular parts of the animator as custom transitions should not effect all parameters of Position.
+            // Typically Delay, Duration and Alphafunction can be custom.
+            FindAndReplaceAnimatorComponentsForProperty(transitionsForCurrentCondition,
+                                                        AnimatableProperties.Position,
+                                                        ref positionTransitionComponents);
+
+            // Size
+            FindAndReplaceAnimatorComponentsForProperty(transitionsForCurrentCondition,
+                                                        AnimatableProperties.Size,
+                                                        ref sizeTransitionComponents);
+
+            // Add animators to the core Animation,
+
+            SetupAnimationForCustomTransitions(transitionsForCurrentCondition, layoutPositionData.Item.Owner);
+
+            SetupAnimationForPosition(layoutPositionData, positionTransitionComponents);
+
+            SetupAnimationForSize(layoutPositionData, sizeTransitionComponents);
+
+            // Dispose components
+            positionTransitionComponents.Dispose();
+            sizeTransitionComponents.Dispose();
+        }
+
+        private void AnimationFinished(object sender, EventArgs e)
+        {
+            // Iterate list of LayoutItem that were set for removal.
+            // Now the core animation has finished their Views can be removed.
+            if (itemRemovalQueue != null)
+            {
+                foreach (LayoutItem item in itemRemovalQueue)
+                {
+                    // Check incase the parent was already removed and the Owner was
+                    // removed already.
+                    if (item.Owner)
+                    {
+                        // Check again incase the parent has already been removed.
+                        if (item.GetParent() is LayoutGroup layoutGroup)
+                        {
+                            layoutGroup.Owner?.RemoveChild(item.Owner);
+                        }
+
+                    }
+                }
+                itemRemovalQueue.Clear();
+                // If LayoutItems added to stack whilst the core animation is playing
+                // they would have been cleared here.
+                // Could have another stack to be added to whilst the animation is running.
+                // After the running stack is cleared it can be populated with the content
+                // of the other stack.  Then the main removal stack iterated when AnimationFinished
+                // occurs again.
+            }
+            NUILog.Debug("LayoutController AnimationFinished");
+            coreAnimation?.Clear();
+        }
+
+        private void SetupAnimationForPosition(LayoutData layoutPositionData, TransitionComponents positionTransitionComponents)
+        {
+            // A removed item does not have a valid target position within the layout so don't try to position.
+            if (layoutPositionData.ConditionForAnimation != TransitionCondition.Remove)
+            {
+                var vector = new Vector3(layoutPositionData.Left,
+                                        layoutPositionData.Top,
+                                        layoutPositionData.Item.Owner.Position.Z);
+                coreAnimation.AnimateTo(layoutPositionData.Item.Owner, "Position",
+                            vector,
+                            positionTransitionComponents.Delay,
+                            positionTransitionComponents.Duration,
+                            positionTransitionComponents.AlphaFunction);
+
+                NUILog.Debug("LayoutController SetupAnimationForPosition View:" + layoutPositionData.Item.Owner.Name +
+                                   " left:" + layoutPositionData.Left +
+                                   " top:" + layoutPositionData.Top +
+                                   " delay:" + positionTransitionComponents.Delay +
+                                   " duration:" + positionTransitionComponents.Duration);
+                vector.Dispose();
+                vector = null;
+            }
+        }
+
+        private void SetupAnimationForSize(LayoutData layoutPositionData, TransitionComponents sizeTransitionComponents)
+        {
+            // Text size cant be animated so is set to it's final size.
+            // It is due to the internals of the Text not being able to recalculate fast enough.
+            if (layoutPositionData.Item.Owner is TextLabel || layoutPositionData.Item.Owner is TextField)
+            {
+                float itemWidth = layoutPositionData.Right - layoutPositionData.Left;
+                float itemHeight = layoutPositionData.Bottom - layoutPositionData.Top;
+                // Set size directly.
+                layoutPositionData.Item.Owner.Size2D = new Size2D((int)itemWidth, (int)itemHeight);
+            }
+            else
+            {
+                var vector = new Vector3(layoutPositionData.Right - layoutPositionData.Left,
+                                                         layoutPositionData.Bottom - layoutPositionData.Top,
+                                                         layoutPositionData.Item.Owner.Position.Z);
+                coreAnimation.AnimateTo(layoutPositionData.Item.Owner, "Size",
+                                         vector,
+                                         sizeTransitionComponents.Delay,
+                                         sizeTransitionComponents.Duration,
+                                         sizeTransitionComponents.AlphaFunction);
+
+                NUILog.Debug("LayoutController SetupAnimationForSize View:" + layoutPositionData.Item.Owner.Name +
+                                   " width:" + (layoutPositionData.Right - layoutPositionData.Left) +
+                                   " height:" + (layoutPositionData.Bottom - layoutPositionData.Top) +
+                                   " delay:" + sizeTransitionComponents.Delay +
+                                   " duration:" + sizeTransitionComponents.Duration);
+                vector.Dispose();
+                vector = null;
+            }
+        }
+    }
+}