737cf271e09d42aa9cc170a92fabadcd44a43b81
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Components / Controls / Navigation / Navigator.cs
1 /*
2  * Copyright(c) 2021 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 using System;
19 using System.Collections.Generic;
20 using System.ComponentModel;
21 using Tizen.NUI.BaseComponents;
22 using Tizen.NUI.Binding;
23
24 namespace Tizen.NUI.Components
25 {
26     /// <summary>
27     /// PoppedEventArgs is a class to record <see cref="Navigator.Popped"/> event arguments which will be sent to user.
28     /// </summary>
29     /// <since_tizen> 9 </since_tizen>
30     public class PoppedEventArgs : EventArgs
31     {
32         /// <summary>
33         /// Page popped by Navigator.
34         /// </summary>
35         /// <since_tizen> 9 </since_tizen>
36         public Page Page { get; internal set; }
37     }
38
39     /// <summary>
40     /// The Navigator is a class which navigates pages with stack methods such as Push and Pop.
41     /// </summary>
42     /// <remarks>
43     /// With Transition class, Navigator supports smooth transition of View pair between two Pages
44     /// by using <see cref="PushWithTransition(Page)"/> and <see cref="PopWithTransition()"/> methods.
45     /// If current top Page and next top Page have <see cref="View"/>s those have same TransitionTag,
46     /// Navigator creates smooth transition motion for them.
47     /// Navigator.Transition property can be used to set properties of the Transition such as TimePeriod and AlphaFunction.
48     /// When all transitions are finished, Navigator calls a callback methods those connected on the "TransitionFinished" event.
49     /// </remarks>
50     /// <example>
51     /// <code>
52     /// Navigator navigator = new Navigator()
53     /// {
54     ///     TimePeriod = new TimePeriod(500),
55     ///     AlphaFunction = new AlphaFunction(AlphaFunction.BuiltinFunctions.EaseInOutSine)
56     /// };
57     ///
58     /// View view = new View()
59     /// {
60     ///     TransitionOptions = new TransitionOptions()
61     ///     {
62     ///         /* Set properties for the transition of this View */
63     ///     }
64     /// };
65     ///
66     /// ContentPage newPage = new ContentPage()
67     /// {
68     ///     Content = view,
69     /// };
70     ///
71     /// Navigator.PushWithTransition(newPage);
72     /// </code>
73     /// </example>
74     /// <since_tizen> 9 </since_tizen>
75     public class Navigator : Control
76     {
77         /// <summary>
78         /// TransitionProperty
79         /// </summary>
80         [EditorBrowsable(EditorBrowsableState.Never)]
81         public static readonly BindableProperty TransitionProperty = BindableProperty.Create(nameof(Transition), typeof(Transition), typeof(Navigator), null, propertyChanged: (bindable, oldValue, newValue) =>
82         {
83             var instance = (Navigator)bindable;
84             if (newValue != null)
85             {
86                 instance.InternalTransition = newValue as Transition;
87             }
88         },
89         defaultValueCreator: (bindable) =>
90         {
91             var instance = (Navigator)bindable;
92             return instance.InternalTransition;
93         });
94
95         private const int DefaultTransitionDuration = 500;
96
97         //This will be replaced with view transition class instance.
98         private Animation curAnimation = null;
99
100         //This will be replaced with view transition class instance.
101         private Animation newAnimation = null;
102
103         private TransitionSet transitionSet = null;
104
105         private Transition transition = new Transition()
106         {
107             TimePeriod = new TimePeriod(DefaultTransitionDuration),
108             AlphaFunction = new AlphaFunction(AlphaFunction.BuiltinFunctions.Default),
109         };
110
111         private bool transitionFinished = true;
112
113         //TODO: Needs to consider how to remove disposed window from dictionary.
114         //Two dictionaries are required to remove disposed navigator from dictionary.
115         private static Dictionary<Window, Navigator> windowNavigator = new Dictionary<Window, Navigator>();
116         private static Dictionary<Navigator, Window> navigatorWindow = new Dictionary<Navigator, Window>();
117
118         private List<Page> navigationPages = new List<Page>();
119
120         /// <summary>
121         /// Creates a new instance of a Navigator.
122         /// </summary>
123         /// <since_tizen> 9 </since_tizen>
124         public Navigator() : base()
125         {
126             Layout = new AbsoluteLayout();
127         }
128
129         /// <inheritdoc/>
130         [EditorBrowsable(EditorBrowsableState.Never)]
131         public override void OnInitialize()
132         {
133             base.OnInitialize();
134
135             SetAccessibilityConstructor(Role.PageTabList);
136         }
137
138         /// <summary>
139         /// An event fired when Transition has been finished.
140         /// </summary>
141         /// <since_tizen> 9 </since_tizen>
142         public event EventHandler<EventArgs> TransitionFinished;
143
144         /// <summary>
145         /// An event fired when Pop of a page has been finished.
146         /// </summary>
147         /// <remarks>
148         /// When you free resources in the Popped event handler, please make sure if the popped page is the page you find.
149         /// </remarks>
150         /// <since_tizen> 9 </since_tizen>
151         public event EventHandler<PoppedEventArgs> Popped;
152
153         /// <summary>
154         /// Returns the count of pages in Navigator.
155         /// </summary>
156         /// <since_tizen> 9 </since_tizen>
157         public int PageCount => navigationPages.Count;
158
159         /// <summary>
160         /// Transition properties for the transition of View pair having same transition tag.
161         /// </summary>
162         /// <since_tizen> 9 </since_tizen>
163         public Transition Transition
164         {
165             get
166             {
167                 return GetValue(TransitionProperty) as Transition;
168             }
169             set
170             {
171                 SetValue(TransitionProperty, value);
172                 NotifyPropertyChanged();
173             }
174         }
175         private Transition InternalTransition
176         {
177             set
178             {
179                 transition = value;
180             }
181             get
182             {
183                 return transition;
184             }
185         }
186
187         /// <summary>
188         /// Pushes a page to Navigator.
189         /// If the page is already in Navigator, then it is not pushed.
190         /// </summary>
191         /// <param name="page">The page to push to Navigator.</param>
192         /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
193         /// <since_tizen> 9 </since_tizen>
194         public void PushWithTransition(Page page)
195         {
196             if (!transitionFinished)
197             {
198                 Tizen.Log.Error("NUI", "Transition is still not finished.\n");
199                 return;
200             }
201
202             if (page == null)
203             {
204                 throw new ArgumentNullException(nameof(page), "page should not be null.");
205             }
206
207             //Duplicate page is not pushed.
208             if (navigationPages.Contains(page)) return;
209
210             var topPage = Peek();
211
212             if (!topPage)
213             {
214                 Insert(0, page);
215                 return;
216             }
217
218             navigationPages.Add(page);
219             Add(page);
220             page.Navigator = this;
221
222             //Invoke Page events
223             page.InvokeAppearing();
224             topPage.InvokeDisappearing();
225
226             transitionSet = CreateTransitions(topPage, page, true);
227             transitionSet.Finished += (object sender, EventArgs e) =>
228             {
229                 if (page is DialogPage == false)
230                 {
231                    topPage.SetVisible(false);
232                 }
233
234                 // Need to update Content of the new page
235                 ShowContentOfPage(page);
236
237                 //Invoke Page events
238                 page.InvokeAppeared();
239                 topPage.InvokeDisappeared();
240                 NotifyAccessibilityStatesChangeOfPages(topPage, page);
241             };
242             transitionFinished = false;
243         }
244
245         /// <summary>
246         /// Pops the top page from Navigator.
247         /// </summary>
248         /// <returns>The popped page.</returns>
249         /// <exception cref="InvalidOperationException">Thrown when there is no page in Navigator.</exception>
250         /// <since_tizen> 9 </since_tizen>
251         public Page PopWithTransition()
252         {
253             if (!transitionFinished)
254             {
255                 Tizen.Log.Error("NUI", "Transition is still not finished.\n");
256                 return null;
257             }
258
259             if (navigationPages.Count == 0)
260             {
261                 throw new InvalidOperationException("There is no page in Navigator.");
262             }
263
264             var topPage = Peek();
265
266             if (navigationPages.Count == 1)
267             {
268                 Remove(topPage);
269
270                 //Invoke Popped event
271                 Popped?.Invoke(this, new PoppedEventArgs() { Page = topPage });
272
273                 return topPage;
274             }
275             var newTopPage = navigationPages[navigationPages.Count - 2];
276
277             //Invoke Page events
278             newTopPage.InvokeAppearing();
279             topPage.InvokeDisappearing();
280
281             transitionSet = CreateTransitions(topPage, newTopPage, false);
282             transitionSet.Finished += (object sender, EventArgs e) =>
283             {
284                 Remove(topPage);
285                 topPage.SetVisible(true);
286
287                 // Need to update Content of the new page
288                 ShowContentOfPage(newTopPage);
289
290                 //Invoke Page events
291                 newTopPage.InvokeAppeared();
292                 topPage.InvokeDisappeared();
293                 NotifyAccessibilityStatesChangeOfPages(topPage, newTopPage);
294
295                 //Invoke Popped event
296                 Popped?.Invoke(this, new PoppedEventArgs() { Page = topPage });
297             };
298             transitionFinished = false;
299
300             return topPage;
301         }
302
303         /// <summary>
304         /// Pushes a page to Navigator.
305         /// If the page is already in Navigator, then it is not pushed.
306         /// </summary>
307         /// <param name="page">The page to push to Navigator.</param>
308         /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
309         /// <since_tizen> 9 </since_tizen>
310         public void Push(Page page)
311         {
312             if (!transitionFinished)
313             {
314                 Tizen.Log.Error("NUI", "Transition is still not finished.\n");
315                 return;
316             }
317
318             if (page == null)
319             {
320                 throw new ArgumentNullException(nameof(page), "page should not be null.");
321             }
322
323             //Duplicate page is not pushed.
324             if (navigationPages.Contains(page)) return;
325
326             var curTop = Peek();
327
328             if (!curTop)
329             {
330                 Insert(0, page);
331                 return;
332             }
333
334             navigationPages.Add(page);
335             Add(page);
336             page.Navigator = this;
337
338             //Invoke Page events
339             page.InvokeAppearing();
340             curTop.InvokeDisappearing();
341
342             //TODO: The following transition codes will be replaced with view transition.
343             InitializeAnimation();
344
345             if (page is DialogPage == false)
346             {
347                 curAnimation = new Animation(1000);
348                 curAnimation.AnimateTo(curTop, "Opacity", 1.0f, 0, 1000);
349                 curAnimation.EndAction = Animation.EndActions.StopFinal;
350                 curAnimation.Finished += (object sender, EventArgs args) =>
351                 {
352                     curTop.SetVisible(false);
353
354                     //Invoke Page events
355                     curTop.InvokeDisappeared();
356                 };
357                 curAnimation.Play();
358
359                 page.Opacity = 0.0f;
360                 page.SetVisible(true);
361                 newAnimation = new Animation(1000);
362                 newAnimation.AnimateTo(page, "Opacity", 1.0f, 0, 1000);
363                 newAnimation.EndAction = Animation.EndActions.StopFinal;
364                 newAnimation.Finished += (object sender, EventArgs e) =>
365                 {
366                     // Need to update Content of the new page
367                     ShowContentOfPage(page);
368
369                     //Invoke Page events
370                     page.InvokeAppeared();
371                     NotifyAccessibilityStatesChangeOfPages(curTop, page);
372                 };
373                 newAnimation.Play();
374             }
375             else
376             {
377                 ShowContentOfPage(page);
378             }
379         }
380
381         /// <summary>
382         /// Pops the top page from Navigator.
383         /// </summary>
384         /// <returns>The popped page.</returns>
385         /// <exception cref="InvalidOperationException">Thrown when there is no page in Navigator.</exception>
386         /// <since_tizen> 9 </since_tizen>
387         public Page Pop()
388         {
389             if (!transitionFinished)
390             {
391                 Tizen.Log.Error("NUI", "Transition is still not finished.\n");
392                 return null;
393             }
394
395             if (navigationPages.Count == 0)
396             {
397                 throw new InvalidOperationException("There is no page in Navigator.");
398             }
399
400             var curTop = Peek();
401
402             if (navigationPages.Count == 1)
403             {
404                 Remove(curTop);
405
406                 //Invoke Popped event
407                 Popped?.Invoke(this, new PoppedEventArgs() { Page = curTop });
408
409                 return curTop;
410             }
411
412             var newTop = navigationPages[navigationPages.Count - 2];
413
414             //Invoke Page events
415             newTop.InvokeAppearing();
416             curTop.InvokeDisappearing();
417
418             //TODO: The following transition codes will be replaced with view transition.
419             InitializeAnimation();
420
421             if (curTop is DialogPage == false)
422             {
423                 curAnimation = new Animation(1000);
424                 curAnimation.AnimateTo(curTop, "Opacity", 0.0f, 0, 1000);
425                 curAnimation.EndAction = Animation.EndActions.StopFinal;
426                 curAnimation.Finished += (object sender, EventArgs e) =>
427                 {
428                     //Removes the current top page after transition is finished.
429                     Remove(curTop);
430                     curTop.Opacity = 1.0f;
431
432                     //Invoke Page events
433                     curTop.InvokeDisappeared();
434
435                     //Invoke Popped event
436                     Popped?.Invoke(this, new PoppedEventArgs() { Page = curTop });
437                 };
438                 curAnimation.Play();
439
440                 newTop.Opacity = 1.0f;
441                 newTop.SetVisible(true);
442                 newAnimation = new Animation(1000);
443                 newAnimation.AnimateTo(newTop, "Opacity", 1.0f, 0, 1000);
444                 newAnimation.EndAction = Animation.EndActions.StopFinal;
445                 newAnimation.Finished += (object sender, EventArgs e) =>
446                 {
447                     // Need to update Content of the new page
448                     ShowContentOfPage(newTop);
449
450                     //Invoke Page events
451                     newTop.InvokeAppeared();
452                     NotifyAccessibilityStatesChangeOfPages(curTop, newTop);
453                 };
454                 newAnimation.Play();
455             }
456             else
457             {
458                 Remove(curTop);
459             }
460
461             return curTop;
462         }
463
464         /// <summary>
465         /// Returns the page of the given index in Navigator.
466         /// The indices of pages in Navigator are basically the order of pushing or inserting to Navigator.
467         /// So a page's index in Navigator can be changed whenever push/insert or pop/remove occurs.
468         /// </summary>
469         /// <param name="index">The index of a page in Navigator.</param>
470         /// <returns>The page of the given index in Navigator.</returns>
471         /// <exception cref="ArgumentOutOfRangeException">Thrown when the argument index is less than 0, or greater than the number of pages.</exception>
472         public Page GetPage(int index)
473         {
474             if ((index < 0) || (index > navigationPages.Count))
475             {
476                 throw new ArgumentOutOfRangeException(nameof(index), "index should be greater than or equal to 0, and less than or equal to the number of pages.");
477             }
478
479             return navigationPages[index];
480         }
481
482         /// <summary>
483         /// Returns the current index of the given page in Navigator.
484         /// The indices of pages in Navigator are basically the order of pushing or inserting to Navigator.
485         /// So a page's index in Navigator can be changed whenever push/insert or pop/remove occurs.
486         /// </summary>
487         /// <param name="page">The page in Navigator.</param>
488         /// <returns>The index of the given page in Navigator. If the given page is not in the Navigator, then -1 is returned.</returns>
489         /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
490         /// <since_tizen> 9 </since_tizen>
491         public int IndexOf(Page page)
492         {
493             if (page == null)
494             {
495                 throw new ArgumentNullException(nameof(page), "page should not be null.");
496             }
497
498             for (int i = 0; i < navigationPages.Count; i++)
499             {
500                 if (navigationPages[i] == page)
501                 {
502                     return i;
503                 }
504             }
505
506             return -1;
507         }
508
509         /// <summary>
510         /// Inserts a page at the specified index of Navigator.
511         /// The indices of pages in Navigator are basically the order of pushing or inserting to Navigator.
512         /// So a page's index in Navigator can be changed whenever push/insert or pop/remove occurs.
513         /// To find the current index of a page in Navigator, please use IndexOf(page).
514         /// If the page is already in Navigator, then it is not inserted.
515         /// </summary>
516         /// <param name="index">The index of a page in Navigator where the page will be inserted.</param>
517         /// <param name="page">The page to insert to Navigator.</param>
518         /// <exception cref="ArgumentOutOfRangeException">Thrown when the argument index is less than 0, or greater than the number of pages.</exception>
519         /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
520         /// <since_tizen> 9 </since_tizen>
521         public void Insert(int index, Page page)
522         {
523             if ((index < 0) || (index > navigationPages.Count))
524             {
525                 throw new ArgumentOutOfRangeException(nameof(index), "index should be greater than or equal to 0, and less than or equal to the number of pages.");
526             }
527
528             if (page == null)
529             {
530                 throw new ArgumentNullException(nameof(page), "page should not be null.");
531             }
532
533             //Duplicate page is not pushed.
534             if (navigationPages.Contains(page)) return;
535
536             //TODO: The following transition codes will be replaced with view transition.
537             InitializeAnimation();
538
539             ShowContentOfPage(page);
540
541             if (index == PageCount)
542             {
543                 page.Opacity = 1.0f;
544                 page.SetVisible(true);
545             }
546             else
547             {
548                 page.SetVisible(false);
549                 page.Opacity = 0.0f;
550             }
551
552             navigationPages.Insert(index, page);
553             Add(page);
554             page.Navigator = this;
555         }
556
557         /// <summary>
558         /// Inserts a page to Navigator before an existing page.
559         /// If the page is already in Navigator, then it is not inserted.
560         /// </summary>
561         /// <param name="before">The existing page, before which a page will be inserted.</param>
562         /// <param name="page">The page to insert to Navigator.</param>
563         /// <exception cref="ArgumentNullException">Thrown when the argument before is null.</exception>
564         /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
565         /// <exception cref="ArgumentException">Thrown when the argument before does not exist in Navigator.</exception>
566         /// <since_tizen> 9 </since_tizen>
567         public void InsertBefore(Page before, Page page)
568         {
569             if (before == null)
570             {
571                 throw new ArgumentNullException(nameof(before), "before should not be null.");
572             }
573
574             if (page == null)
575             {
576                 throw new ArgumentNullException(nameof(page), "page should not be null.");
577             }
578
579             //Find the index of before page.
580             int beforeIndex = navigationPages.FindIndex(x => x == before);
581
582             //before does not exist in Navigator.
583             if (beforeIndex == -1)
584             {
585                 throw new ArgumentException("before does not exist in Navigator.", nameof(before));
586             }
587
588             Insert(beforeIndex, page);
589         }
590
591         /// <summary>
592         /// Removes a page from Navigator.
593         /// </summary>
594         /// <param name="page">The page to remove from Navigator.</param>
595         /// <exception cref="ArgumentNullException">Thrown when the argument page is null.</exception>
596         /// <since_tizen> 9 </since_tizen>
597         public void Remove(Page page)
598         {
599             if (page == null)
600             {
601                 throw new ArgumentNullException(nameof(page), "page should not be null.");
602             }
603
604             //TODO: The following transition codes will be replaced with view transition.
605             InitializeAnimation();
606
607             HideContentOfPage(page);
608
609             if ((page == Peek()) && (PageCount >= 2))
610             {
611                 navigationPages[PageCount - 2].Opacity = 1.0f;
612                 navigationPages[PageCount - 2].SetVisible(true);
613             }
614
615             page.Navigator = null;
616             navigationPages.Remove(page);
617             base.Remove(page);
618         }
619
620         /// <summary>
621         /// Removes a page at the specified index of Navigator.
622         /// The indices of pages in Navigator are basically the order of pushing or inserting to Navigator.
623         /// So a page's index in Navigator can be changed whenever push/insert or pop/remove occurs.
624         /// To find the current index of a page in Navigator, please use IndexOf(page).
625         /// </summary>
626         /// <param name="index">The index of a page in Navigator where the page will be removed.</param>
627         /// <exception cref="ArgumentOutOfRangeException">Thrown when the index is less than 0, or greater than or equal to the number of pages.</exception>
628         /// <since_tizen> 9 </since_tizen>
629         public void RemoveAt(int index)
630         {
631             if ((index < 0) || (index >= navigationPages.Count))
632             {
633                 throw new ArgumentOutOfRangeException(nameof(index), "index should be greater than or equal to 0, and less than the number of pages.");
634             }
635
636             Remove(navigationPages[index]);
637         }
638
639         /// <summary>
640         /// Returns the page at the top of Navigator.
641         /// </summary>
642         /// <returns>The page at the top of Navigator.</returns>
643         /// <since_tizen> 9 </since_tizen>
644         public Page Peek()
645         {
646             if (navigationPages.Count == 0) return null;
647
648             return navigationPages[navigationPages.Count - 1];
649         }
650
651         /// <summary>
652         /// Disposes Navigator and all children on it.
653         /// </summary>
654         /// <param name="type">Dispose type.</param>
655         [EditorBrowsable(EditorBrowsableState.Never)]
656         protected override void Dispose(DisposeTypes type)
657         {
658             if (disposed)
659             {
660                 return;
661             }
662
663             if (type == DisposeTypes.Explicit)
664             {
665                 foreach (Page page in navigationPages)
666                 {
667                     Utility.Dispose(page);
668                 }
669                 navigationPages.Clear();
670
671                 Window window;
672
673                 if (navigatorWindow.TryGetValue(this, out window) == true)
674                 {
675                     navigatorWindow.Remove(this);
676                     windowNavigator.Remove(window);
677                 }
678             }
679
680             base.Dispose(type);
681         }
682
683         /// <summary>
684         /// Returns the default navigator of the given window.
685         /// </summary>
686         /// <returns>The default navigator of the given window.</returns>
687         /// <exception cref="ArgumentNullException">Thrown when the argument window is null.</exception>
688         /// <since_tizen> 9 </since_tizen>
689         public static Navigator GetDefaultNavigator(Window window)
690         {
691             if (window == null)
692             {
693                 throw new ArgumentNullException(nameof(window), "window should not be null.");
694             }
695
696             if (windowNavigator.ContainsKey(window) == true)
697             {
698                 return windowNavigator[window];
699             }
700
701             var defaultNavigator = new Navigator();
702             defaultNavigator.WidthResizePolicy = ResizePolicyType.FillToParent;
703             defaultNavigator.HeightResizePolicy = ResizePolicyType.FillToParent;
704             window.Add(defaultNavigator);
705             windowNavigator.Add(window, defaultNavigator);
706             navigatorWindow.Add(defaultNavigator, window);
707
708             return defaultNavigator;
709         }
710
711         /// <summary>
712         /// Create Transitions between currentTopPage and newTopPage
713         /// </summary>
714         /// <param name="currentTopPage">The top page of Navigator.</param>
715         /// <param name="newTopPage">The new top page after transition.</param>
716         /// <param name="pushTransition">True if this transition is for push new page</param>
717         private TransitionSet CreateTransitions(Page currentTopPage, Page newTopPage, bool pushTransition)
718         {
719             currentTopPage.SetVisible(true);
720             newTopPage.SetVisible(true);
721
722             List<View> taggedViewsInNewTopPage = new List<View>();
723             RetrieveTaggedViews(taggedViewsInNewTopPage, newTopPage, true);
724             List<View> taggedViewsInCurrentTopPage = new List<View>();
725             RetrieveTaggedViews(taggedViewsInCurrentTopPage, currentTopPage, true);
726
727             List<KeyValuePair<View, View>> sameTaggedViewPair = new List<KeyValuePair<View, View>>();
728             foreach(View currentTopPageView in taggedViewsInCurrentTopPage)
729             {
730                 bool findPair = false;
731                 foreach(View newTopPageView in taggedViewsInNewTopPage)
732                 {
733                     if((currentTopPageView.TransitionOptions != null) && (newTopPageView.TransitionOptions != null) &&
734                         currentTopPageView.TransitionOptions?.TransitionTag == newTopPageView.TransitionOptions?.TransitionTag)
735                     {
736                         sameTaggedViewPair.Add(new KeyValuePair<View, View>(currentTopPageView, newTopPageView));
737                         findPair = true;
738                         break;
739                     }
740                 }
741                 if(findPair)
742                 {
743                     taggedViewsInNewTopPage.Remove(sameTaggedViewPair[sameTaggedViewPair.Count - 1].Value);
744                 }
745             }
746             foreach(KeyValuePair<View, View> pair in sameTaggedViewPair)
747             {
748                 taggedViewsInCurrentTopPage.Remove(pair.Key);
749             }
750
751             TransitionSet newTransitionSet = new TransitionSet();
752             foreach(KeyValuePair<View, View> pair in sameTaggedViewPair)
753             {
754                 TransitionItem pairTransition = transition.CreateTransition(pair.Key, pair.Value, pushTransition);
755                 if(pair.Value.TransitionOptions?.TransitionWithChild ?? false)
756                 {
757                     pairTransition.TransitionWithChild = true;
758                 }
759                 newTransitionSet.AddTransition(pairTransition);
760             }
761
762             newTransitionSet.Finished += (object sender, EventArgs e) =>
763             {
764                 if(newTopPage.Layout != null)
765                 {
766                     newTopPage.Layout.RequestLayout();
767                 }
768                 if(currentTopPage.Layout != null)
769                 {
770                     currentTopPage.Layout.RequestLayout();
771                 }
772                 transitionFinished = true;
773                 InvokeTransitionFinished();
774                 transitionSet.Dispose();
775                 currentTopPage.Opacity = 1.0f;
776             };
777
778             if (!pushTransition || newTopPage is DialogPage == false)
779             {
780                 View transitionView = (currentTopPage is ContentPage) ? (currentTopPage as ContentPage).Content : (currentTopPage as DialogPage).Content;
781                 if (currentTopPage.DisappearingTransition != null && transitionView != null)
782                 {
783                     TransitionItemBase disappearingTransition = currentTopPage.DisappearingTransition.CreateTransition(transitionView, false);
784                     disappearingTransition.TransitionWithChild = true;
785                     newTransitionSet.AddTransition(disappearingTransition);
786                 }
787                 else
788                 {
789                     currentTopPage.SetVisible(false);
790                 }
791             }
792             if (pushTransition || currentTopPage is DialogPage == false)
793             {
794                 View transitionView = (newTopPage is ContentPage) ? (newTopPage as ContentPage).Content : (newTopPage as DialogPage).Content;
795                 if (newTopPage.AppearingTransition != null && transitionView != null)
796                 {
797                     TransitionItemBase appearingTransition = newTopPage.AppearingTransition.CreateTransition(transitionView, true);
798                     appearingTransition.TransitionWithChild = true;
799                     newTransitionSet.AddTransition(appearingTransition);
800                 }
801             }
802
803             newTransitionSet.Play();
804
805             return newTransitionSet;
806         }
807
808         /// <summary>
809         /// Retrieve Tagged Views in the view tree.
810         /// </summary>
811         /// <param name="taggedViews">Returned tagged view list..</param>
812         /// <param name="view">Root View to get tagged child View.</param>
813         /// <param name="isRoot">Flag to check current View is page or not</param>
814         private void RetrieveTaggedViews(List<View> taggedViews, View view, bool isRoot)
815         {
816             if (!isRoot && view.TransitionOptions != null)
817             {
818                 if (!string.IsNullOrEmpty(view.TransitionOptions?.TransitionTag))
819                 {
820                     taggedViews.Add((view as View));
821                     if (view.TransitionOptions.TransitionWithChild)
822                     {
823                         return;
824                     }
825                 }
826
827             }
828
829             foreach (View child in view.Children)
830             {
831                 RetrieveTaggedViews(taggedViews, child, false);
832             }
833         }
834
835         /// <summary>
836         /// Notify accessibility states change of pages.
837         /// </summary>
838         /// <param name="disappearedPage">Disappeared page</param>
839         /// <param name="appearedPage">Appeared page</param>
840         private void NotifyAccessibilityStatesChangeOfPages(Page disappearedPage, Page appearedPage)
841         {
842             if (disappearedPage != null)
843             {
844                 disappearedPage.UnregisterDefaultLabel();
845                 //We can call disappearedPage.NotifyAccessibilityStatesChange
846                 //To reduce accessibility events, we are using currently highlighted view instead
847                 View curHighlightedView = Accessibility.Accessibility.Instance.GetCurrentlyHighlightedView();
848                 if (curHighlightedView != null)
849                 {
850                     curHighlightedView.NotifyAccessibilityStatesChange(AccessibilityStates.Visible | AccessibilityStates.Showing, AccessibilityStatesNotifyMode.Single);
851                 }
852             }
853
854             if (appearedPage != null)
855             {
856                 appearedPage.RegisterDefaultLabel();
857                 appearedPage.NotifyAccessibilityStatesChange(AccessibilityStates.Visible | AccessibilityStates.Showing, AccessibilityStatesNotifyMode.Single);
858             }
859         }
860
861         internal void InvokeTransitionFinished()
862         {
863             TransitionFinished?.Invoke(this, new EventArgs());
864         }
865
866         //TODO: The following transition codes will be replaced with view transition.
867         private void InitializeAnimation()
868         {
869             if (curAnimation != null)
870             {
871                 curAnimation.Stop();
872                 curAnimation.Clear();
873                 curAnimation = null;
874             }
875
876             if (newAnimation != null)
877             {
878                 newAnimation.Stop();
879                 newAnimation.Clear();
880                 newAnimation = null;
881             }
882         }
883
884         // Show and Register Content of Page to Accessibility bridge
885         private void ShowContentOfPage(Page page)
886         {
887             View content = (page is DialogPage) ? (page as DialogPage)?.Content : (page as ContentPage)?.Content;
888             if (content != null)
889             {
890                 content.Show(); // Calls RegisterDefaultLabel()
891             }
892         }
893
894         // Hide and Remove Content of Page from Accessibility bridge
895         private void HideContentOfPage(Page page)
896         {
897             View content = (page is DialogPage) ? (page as DialogPage)?.Content : (page as ContentPage)?.Content;
898             if (content != null)
899             {
900                 content.Hide(); // Calls UnregisterDefaultLabel()
901             }
902         }
903     }
904 }