Skip to content

Instantly share code, notes, and snippets.

@marcinkuptel
Created October 24, 2015 16:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save marcinkuptel/85435f988d444390f20a to your computer and use it in GitHub Desktop.
Save marcinkuptel/85435f988d444390f20a to your computer and use it in GitHub Desktop.
ProxyView - Advanced UI with Xamarin.iOS
using System;
using System.Collections.Generic;
using UIKit;
using CoreGraphics;
using Foundation;
namespace iOS
{
public class ProxyView: UIView
{
enum SubviewTag {
Container = 1,
Content
}
UIScrollView ParentScrollView { get; set; }
UIView ParentContainerView { get; set; }
List<UIScrollView> ScrollViews { get; set; }
List<int> _contentHeights;
public List<int> ContentHeights {
get {
return _contentHeights;
}
set {
if(_contentHeights == null) {
_contentHeights = value;
} else {
if(value.Count != _contentHeights.Count) {
_contentHeights = value;
ReloadScrollViews();
}else{
_contentHeights = value;
UpdateContentHeights();
}
}
}
}
int HeaderHeight { get; set; }
int _pageIndex;
public int PageIndex {
get {
return _pageIndex;
}
set {
_pageIndex = value;
SetNeedsLayout();
}
}
WeakReference<IProxyViewDelegate> _proxyDelegate;
IProxyViewDelegate ProxyDelegate {
get {
IProxyViewDelegate proxyDelegate;
_proxyDelegate.TryGetTarget(out proxyDelegate);
return proxyDelegate;
}
set {
_proxyDelegate = new WeakReference<IProxyViewDelegate>(value);
}
}
public ProxyView (IProxyViewDelegate proxyDelegate, List<int> contentHeights, int headerHeight)
{
HeaderHeight = headerHeight;
ScrollViews = new List<UIScrollView>();
ContentHeights = contentHeights;
ProxyDelegate = proxyDelegate;
SetUpHierarchy();
}
public override void LayoutSubviews()
{
base.LayoutSubviews();
SetPage(PageIndex);
}
void SetUpHierarchy()
{
AddParentScrollView();
AddParentContainerView();
AddChildScrollViews();
}
void AddParentScrollView()
{
ParentScrollView = new UIScrollView();
ParentScrollView.PagingEnabled = true;
ParentScrollView.TranslatesAutoresizingMaskIntoConstraints = false;
ParentScrollView.ShowsHorizontalScrollIndicator = false;
var tap = new UITapGestureRecognizer();
tap.AddTarget(() => {
var loc = tap.LocationInView(this);
ProxyDelegate.ProxyTappedAtLocation(this, loc);
});
AddGestureRecognizer(tap);
var weak = new WeakReference<ProxyView>(this);
ParentScrollView.Scrolled += (sender, e) => {
ProxyView weakProxy;
weak.TryGetTarget(out weakProxy);
_pageIndex = weakProxy.CurrentPageIndex();
ProxyDelegate.ParentScrollViewDidScroll(ParentScrollView.ContentOffset.X);
};
AddSubview(ParentScrollView);
var views = new NSDictionary("parent", ParentScrollView);
var parentVConstraints = NSLayoutConstraint.FromVisualFormat("V:|[parent]|", 0, null, views);
var parentHConstraints = NSLayoutConstraint.FromVisualFormat("H:|[parent]|", 0, null, views);
AddConstraints(parentVConstraints);
AddConstraints(parentHConstraints);
}
void AddParentContainerView()
{
ParentContainerView = new UIView();
ParentContainerView.TranslatesAutoresizingMaskIntoConstraints = false;
var containerViews = new NSDictionary("container", ParentContainerView);
ParentScrollView.AddSubview(ParentContainerView);
var containerVConstraints = NSLayoutConstraint.FromVisualFormat("V:|[container]|", 0, null, containerViews);
var containerHConstraints = NSLayoutConstraint.FromVisualFormat("H:|[container]|", 0, null, containerViews);
var top = NSLayoutConstraint.Create(ParentContainerView, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this, NSLayoutAttribute.Top, 1, 0);
var bottom = NSLayoutConstraint.Create(ParentContainerView, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, this, NSLayoutAttribute.Bottom, 1, 0);
ParentScrollView.AddConstraints(containerVConstraints);
ParentScrollView.AddConstraints(containerHConstraints);
AddConstraint(top);
AddConstraint(bottom);
}
void AddChildScrollViews()
{
for(int i = 0; i < ContentHeights.Count; i++)
{
int height = ContentHeights[i];
var scrollView = AddChildScrollView(i);
var container = AddChildContainerView(scrollView, height);
AddChildContentView(container, height);
}
AddScrollViewConstraints();
}
UIScrollView AddChildScrollView(int index)
{
var scrollView = new UIScrollView();
scrollView.TranslatesAutoresizingMaskIntoConstraints = false;
ScrollViews.Add(scrollView);
ParentContainerView.AddSubview(scrollView);
scrollView.Scrolled += (sender, e) => {
if(scrollView.ContentOffset.Y <= HeaderHeight) {
foreach(UIScrollView s in ScrollViews) {
if(s != scrollView) {
var newOffset = new CGPoint(s.ContentOffset.X, scrollView.ContentOffset.Y);
s.Bounds = new CGRect(newOffset, s.Bounds.Size);
}
}
}
ProxyDelegate.ScrollViewDidScroll(index, scrollView.ContentOffset.Y);
};
var width = NSLayoutConstraint.Create(scrollView, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this, NSLayoutAttribute.Width, 1, 0);
var height = NSLayoutConstraint.Create(scrollView, NSLayoutAttribute.Height, NSLayoutRelation.Equal, ParentContainerView, NSLayoutAttribute.Height, 1, 0);
AddConstraint(width);
ParentContainerView.AddConstraint(height);
return scrollView;
}
UIView AddChildContainerView(UIScrollView childScrollView, int height)
{
var containerView = new UIView();
containerView.Tag = (int)SubviewTag.Container;
containerView.TranslatesAutoresizingMaskIntoConstraints = false;
childScrollView.AddSubview(containerView);
var containerViews = new NSDictionary("container", containerView);
var containerVConstraints = NSLayoutConstraint.FromVisualFormat("V:|[container]|", 0, null, containerViews);
var containerHConstraints = NSLayoutConstraint.FromVisualFormat("H:|[container]|", 0, null, containerViews);
childScrollView.AddConstraints(containerVConstraints);
childScrollView.AddConstraints(containerHConstraints);
return containerView;
}
void AddChildContentView(UIView containerView, int height)
{
var content = new UIView();
content.Tag = (int)SubviewTag.Content;
content.TranslatesAutoresizingMaskIntoConstraints = false;
containerView.AddSubview(content);
var leftContentWidth = NSLayoutConstraint.Create(content, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this, NSLayoutAttribute.Width, 1, 0);
var leftContentHeight = NSLayoutConstraint.Create(content, NSLayoutAttribute.Height, NSLayoutRelation.Equal, null, NSLayoutAttribute.NoAttribute, 1, height);
var contentView = new NSDictionary("content", content);
var vertical = NSLayoutConstraint.FromVisualFormat("V:|[content]|", 0, null, contentView);
var horizontal = NSLayoutConstraint.FromVisualFormat("H:|[content]|", 0, null, contentView);
containerView.AddConstraints(vertical);
containerView.AddConstraints(horizontal);
AddConstraint(leftContentWidth);
containerView.AddConstraint(leftContentHeight);
}
void AddScrollViewConstraints()
{
for(int i = 1; i < ScrollViews.Count; i++)
{
var left = ScrollViews[i - 1];
var right = ScrollViews[i];
var scrollViews = new NSDictionary("left", left, "right", right);
var constraints = NSLayoutConstraint.FromVisualFormat("H:[left][right]", 0, null, scrollViews);
ParentContainerView.AddConstraints(constraints);
}
var leftConstraint = NSLayoutConstraint.FromVisualFormat("H:|[scrollView]", 0, null, new NSDictionary("scrollView", ScrollViews[0]));
ParentContainerView.AddConstraints(leftConstraint);
var last = ScrollViews[ScrollViews.Count - 1];
var rightConstraint = NSLayoutConstraint.FromVisualFormat("H:[scrollView]|", 0, null, new NSDictionary("scrollView", last));
ParentContainerView.AddConstraints(rightConstraint);
}
void UpdateContentHeights()
{
for(int i = 0; i < ScrollViews.Count; i++)
{
var sv = ScrollViews[i];
var height = ContentHeights[i];
var container = sv.ViewWithTag((int)SubviewTag.Container);
var content = container.ViewWithTag((int)SubviewTag.Content);
content.RemoveFromSuperview();
AddChildContentView(container, height);
}
}
void ReloadScrollViews()
{
foreach(UIScrollView s in ScrollViews){
s.RemoveFromSuperview();
}
ScrollViews.Clear();
AddChildScrollViews();
}
#region -
void SetPage(int index)
{
if(index != CurrentPageIndex()) {
var location = new CGPoint(index * ParentScrollView.Frame.Size.Width, ParentScrollView.Bounds.Location.Y);
ParentScrollView.Bounds = new CGRect(location, ParentScrollView.Bounds.Size);
}
}
int CurrentPageIndex()
{
return (int)((ParentScrollView.ContentOffset.X + ParentScrollView.Frame.Size.Width / 2)
/ ParentScrollView.Frame.Size.Width);
}
#endregion
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment