2015-08-28 7 views
1

очень первый вопрос SO после долгого скрытия. Я преподавал разработку iOS с использованием C# под Xamarin с Visual Studio.UIScrollView не регистрирует события, пока ViewController Segue

Мое заявление о проблемах: Почему мое прокручивание работает только, то есть регистрирует такие события, как перетаскивание, ПОСЛЕ того, чтобы использовать ViewController segue для другого вида и обратно? В то же время, используя прокрутку через PageControl работает отлично в любое время?

Я успешно использовал широкий диапазон образцов кода, найденных как здесь, так и на сайте Xamarin и в других местах (включая некоторые образцы, написанные в Swift, которые я перевел на C# - Objective-C, также может быть Minoan Linear B).

Это также работало без проблем во всех вариантах, пока я не начал добавлять AutoLayout в уравнение. Использование FluentLayout упростило это, и теперь у меня в целом есть хорошо продуманный пользовательский интерфейс именно так, как я хочу, на всех моделях iPhone.

Существенный код шаблона работал нормально, когда он все еще был застрял в ViewDidLoad корневого viewcontroller, но теперь, когда я сломал все это на «чистые» классы, он показывает это странное поведение.

Одна из проблем, с которой мне пришлось столкнуться, заключалась в том, что изображение было правильно изменено в кадре UIImageView, когда размер не доступен до тех пор, пока ограничения компоновки не будут завершены. Как я понимаю теперь, концепция ViewDidLayoutSubViews().

Таким образом, мой корень VS кода:

namespace ScrollTest.iOS 
{ 
public partial class RootViewController : UIViewController 
{ 
    ... 

    public override void ViewDidLoad() 
    { 
     base.ViewDidLoad(); 

     View = new MainView(); 
    } 

    public override void ViewDidLayoutSubviews() 
    { 
     base.ViewDidLayoutSubviews(); 

     CGRect pageFrame = SharedStatic.ScrollView.Frame; 

     SharedStatic.ScrollView.ContentSize = new CGSize(pageFrame.Width * 2, pageFrame.Height); 

     SharedStatic.ImageView.Frame = pageFrame; 
     SharedStatic.ImageView.Image = UIImage.FromFile("toothless.jpg").Scale(pageFrame.Size); 
     SharedStatic.ImageView.ContentMode = UIViewContentMode.ScaleAspectFit; 

     pageFrame.X += SharedStatic.ScrollView.Frame.Width; 

     SharedStatic.TableView.Frame = pageFrame; 
     SharedStatic.TableView.ContentMode = UIViewContentMode.ScaleAspectFit; 
    } 
... 
} 

Из класса MainView() на вниз У меня есть иерархия взглядов, которые экземпляры, а затем установить ограничение макета, например, так:

namespace ScrollTest.iOS 
{ 
[Register("MainView")] 
internal class MainView : UIView 
{ 
    public MainView() 
    { 
     Initialise(); 
    } 

    public MainView(CGRect frame) : base(frame) 
    { 
     Initialise(); 
    } 

    private void Initialise() 
    { 
     SharedStatic.MainView = this; 

     var navView = new NavView(); 
     Add(navView); 

     var contentView = new ContentView(); 
     Add(contentView); 

     var pagerView = new PagerView(); 
     Add(pagerView); 

     var toolView = new ToolView(); 
     Add(toolView); 

     var buttonView = new ButtonView(); 
     Add(buttonView); 

     const int padding = 1; 
     var navHeight = 32; 
     var pageControlHeight = 25; 
     var toolHeight = 25; 
     var buttonHeight = 20; 

     this.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints(); 
     this.AddConstraints 
     (
      ... other view constraints... 

      contentView.Below(navView, padding), 
      contentView.AtLeftOf(this), 
      contentView.WithSameWidth(this), 
      contentView.Bottom().EqualTo().TopOf(pagerView), 

      pagerView.Above(toolView, padding), 
      pagerView.AtLeftOf(this), 
      pagerView.WithSameWidth(this), 
      pagerView.Height().EqualTo(pageControlHeight), 

      ... 
     ); 
    } 
} 
} 

Фактический класс ScrollView выглядит так: обработчики событий просто подключаются к отладке вывода для этого теста.

namespace ScrollTest.iOS 
{ 
[Register("ContentView")] 
internal class ContentView : UIScrollView 
{ 
    private ContainerView _containerView; 

    public ContentView() 
    { 
     Initialise(); 
    } 

    public ContentView(CGRect frame) : base(frame) 
    { 
     Initialise(); 
    } 

    private void Initialise() 
    { 
     SharedStatic.ScrollView = this; 

     _containerView = ContainerView.Instance; 
     Add(_containerView); 

     PagingEnabled = true; 
     ScrollEnabled = true; 
     Bounces = false; 
     DirectionalLockEnabled = true; 

     DecelerationEnded += scrollView_DecelerationEnded; 

     Scrolled += delegate { Console.WriteLine("scrolled"); }; 
     DecelerationStarted += delegate { Console.WriteLine("deceleration started"); }; 
     DidZoom += delegate { Console.WriteLine("did zoon"); }; 
     DraggingEnded += delegate { Console.WriteLine("dragging ended"); }; 
     DraggingStarted += delegate { Console.WriteLine("dragging started"); }; 
     ScrollAnimationEnded += delegate { Console.WriteLine("Scroll animation ended"); }; 
     ScrolledToTop += delegate { Console.WriteLine("Scrolled to top"); }; 
     WillEndDragging += delegate { Console.WriteLine("will end dragging"); }; 
     ZoomingEnded += delegate { Console.WriteLine("zooming ended"); }; 
     ZoomingStarted += delegate { Console.WriteLine("zooming started"); }; 

     this.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints(); 
     this.AddConstraints 
     (
      _containerView.AtLeftOf(this), 
      _containerView.AtRightOf(this), 
      _containerView.AtTopOf(this), 
      _containerView.AtBottomOf(this) 
     ); 
    } 

    private void scrollView_DecelerationEnded(object sender, EventArgs e) 
    { 
     Console.WriteLine("Done changing page"); 
     nfloat x1 = SharedStatic.ImageView.Frame.X; 
     nfloat x2 = SharedStatic.TableView.Frame.X; 
     nfloat x = this.ContentOffset.X; 
     if (x == x1) 
     { 
      Console.WriteLine("flip"); 
      SharedStatic.PageControl.CurrentPage = 0; 
     } 
     else 
     { 
      Console.WriteLine("flop"); 
      SharedStatic.PageControl.CurrentPage = 1; 
     } 
    } 
} 
} 

И «после» это вид контейнера, который удерживает мое изображение и таблицу, от моего понимания прошивки кодирования практики является способом сделать это (кроме класса одноплодного, который был результатом тестирования что-то еще).

namespace ScrollTest.iOS 
{ 
[Register("ContainerView")] 
internal sealed class ContainerView : UIView 
{ 
    private UIImageView _imageView; 
    private UITableView _tableView; 

    private static readonly Lazy<ContainerView> lazy = new Lazy<ContainerView>(() => new ContainerView()); 

    public static ContainerView Instance { get { return lazy.Value; } } 

    private ContainerView() 
    { 
     Initialise(); 
    } 

    private ContainerView(CGRect frame) : base(frame) 
    { 
     Initialise(); 
    } 

    private void Initialise() 
    { 
     SharedStatic.ContainerView = this; 

     Console.WriteLine("setting up scrolling stuff"); 

     _imageView = new UIImageView(); 
     Add(_imageView); 
     SharedStatic.ImageView = _imageView; 

     _tableView = new UITableView(); 
     Add(_tableView); 
     SharedStatic.TableView = _tableView; 

     this.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints(); 
     this.AddConstraints 
     (
      _imageView.AtLeftOf(this), 
      _imageView.AtTopOf(this), 
      _imageView.AtBottomOf(this), 
      _imageView.WithSameWidth(this), 
      _tableView.Left().EqualTo().RightOf(_imageView), 
      _tableView.WithSameTop(_imageView), 
      _tableView.WithSameBottom(_imageView), 
      _tableView.WithSameWidth(_imageView) 
     ); 
    } 
} 
} 

На верхней части моего экрана находится панель навигации с кнопкой гамбургера, который вызовет переход к другому ViewController, который по дороге отвечает за выскакивает экран Параметры. Он содержит еще одну кнопку гамбургера, чтобы уволить себя и вернуться на главный экран. Это работает без проблем:

_navBar.SetItems(new UINavigationItem[] { new UINavigationItem { LeftBarButtonItem = new UIBarButtonItem(UIImage.FromFile("hamburger32x32.png"),  UIBarButtonItemStyle.Plain, BringUpOptions) } }, false); 

И обработчик события:

internal async void BringUpOptions(object s, EventArgs e) 
    { 
     Console.WriteLine("Options clicked"); 
     var board = UIStoryboard.FromName("MainStoryboard", null); 
     var optionController = board.InstantiateViewController("OptionsViewController"); 
     optionController.ModalTransitionStyle = UIModalTransitionStyle.FlipHorizontal; 
     await this.Window.RootViewController.PresentViewControllerAsync(optionController, true); 
    } 

Я использую раскадровку редактор только для моих двух контроллеров зрения и Segue. Все остальные элементы управления/представления программно генерируются, как и макеты.

Вводя это вместе, результаты в правильно выложенном пользовательском интерфейсе, правильно работающем сегменте на экране параметров, правильном увольнении/возврате.

Также работает щелчок по элементу управления страницей: перелистывание назад и вперед между изображением и представлением таблицы.

Однако этот пейджинг не работает при попытке перетащить изображение в режим просмотра таблицы! Не при запуске приложения! Отладка показывает, что никаких событий не обнаружено.

До тех пор, пока я не перейду на экран параметров хотя бы один раз, а затем обратно! Затем все работает так, как ожидалось: контроль страниц flip/flops и scrollview свитки вперед и назад.

У меня полная потеря, почему это будет так! Что такое инициализация начального сегмента, которая, по-видимому, необходима для работы scrollview? Я оштукатурил свой код с отладочным выходом и одним нажал на отвлечение. Я просто не могу его найти. Может быть, это ошибка в Xamarin?

Есть ли какие-то настройки или звонки, которые мне нужно сделать из классов вида, о которых я просто не знаю, потому что они обычно скрыты при использовании редакторов раскадровки? Я был над досье класса UIScrollView от Xamarin, но на меня ничего не выпрыгивает.

Я думал, что мое несколько неудобное использование этого общего статического класса может быть проблемой, но я не знаю, как получить информацию о макете на разных классах (поэтому сбой изображений не получается). Мое намерение состояло в том, чтобы погрузиться в это и удалить кладж однажды я решил эту мой остановлю всю работу:

namespace ScrollTest.iOS 
{ 
internal static class SharedStatic 
{ 
    internal static MainView MainView { get; set; } 
    internal static UIPageControl PageControl { get; set; } 
    internal static ContentView ScrollView { get; set; } 
    internal static ContentView ContentView => ScrollView; 
    internal static ContainerView ContainerView { get; set; } 

    internal static UIImageView ImageView { get; set; } 
    internal static UITableView TableView { get; set; } 
} 
} 

С кодом так близко к прекрасно работает, я действительно думал, что я что-то достаточно прямо отсутствую -вперед. Некоторая ошибка новичка, с которой никто не сталкивается при использовании раскадровки, но которая как-то кусает меня.

Спасибо!

EDIT 20150828: После некоторых дальнейших исследований я добавил переопределение для SendEvent для UIApplication:

[Register("TestApp")] 
class TestApp : UIApplication 
{ 
    public override void SendEvent(UIEvent uievent) 
    { 
     base.SendEvent(uievent); 
     Console.WriteLine("event hit"); 
    } 
} 

И затем изменил Main.cs на:

public class Application 
{ 
    // This is the main entry point of the application. 
    static void Main(string[] args) 
    { 
     UIApplication.Main(args, "TestApp", "AppDelegate"); 
    } 
} 

В результате я действительно может см., что прикосновения/перетаскивания/прокрутки приводят к хитам событий, но что они не определены/категоризированы должным образом. Свернув в отладчик, я смог увидеть, что UIScrollView получает «хиты», но просто ничего не делает с ними! По сути, они полностью игнорируют их - пока я не выполнил этот первый сеанс контроллера segue, тогда все события срабатывают правильно.

Это начинает приносить ошибку, но я просто не знаю достаточно, чтобы быть уверенным, что это не где-то в моем коде или на самом деле в Xamarin.

ответ

0

Мне было много проклятия и крика, но я смог решить проблему, получить новые проблемы с устранением неполадок и получить весь целевой интерфейс пользовательского интерфейса для работы. Надеюсь, мое решение поможет другим.

Во-первых, в конце концов, было абсолютно необходимо, чтобы я обнял каждую деталь часто упоминаемых технологов Apple в UIScrollView и Autolayout.

Apple Technical Note TN2154

Это было трудно, потому что я не знаю, Objective-C, но сводила на моем невежестве через ходит взад и вперед с моей Xamarin C# код и образец кода Apple, я был в состоянии развернуть и изучать и действительно понимать взаимодействие между прокруткой и ограничениями автоматической компоновки. Мое нежелание погрузиться в эту синусоидальность, возможно, способствовало тому, что мне нужно было решить эту проблему.

Хотя есть ссылки gazillion, показывающие образцы, многие используют раскадровку, что на самом деле усложнило мою жизнь. YMMV. Как и прежде, я решил взять программный маршрут и использовать чрезвычайно полезным

Cirrious FluentLayout on Github

Ядро моего решения, в конце концов, было использование перекрывая LayoutSubviews() для моей Scrollview и суб-вид контейнера класс, который содержит установку ограничений (помимо некоторых других рефакторингов, чтобы привести все в один файл класса, с использованием «частичного» для разложения кода оптически).

Для UIScrollView я использую один встроенный UIView для контейнера. Ctor объявляет только класс, но макет этого контейнера рассматривается в методе LayoutSubviews()!

[Register("ScrollView")] 
    internal sealed class ScrollView : UIScrollView 
    { 
     internal ContainerView Container { get; } 

     internal ScrollView() 
     { 
      Container = new ContainerView(); 
      Add(Container); 
     } 

     public override void LayoutSubviews() 
     { 
      base.LayoutSubviews(); 

      this.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints(); 
      this.AddConstraints 
      (
       Container.AtTopOf(this), 
       Container.AtLeftOf(this), 
       Container.WithSameWidth(this), 
       Container.WithSameHeight(this) 
      ); 
     } 
    } 

Я повторил это за мнения, содержащиеся в моих 2 прокруткой страниц:

[Register("ContainerView")] 
    internal sealed class ContainerView : UIView 
    { 
     private UIImageView _arenaView; 
     internal UIImageView Arena => _arenaView; 

     private UIView _gridView; 
     internal UIView Grid => _gridView; 

     internal ContainerView() 
     { 
      _arenaView = new UIImageView(); 
      Add(_arenaView); 

      _gridView = new UIView() 
      Add(_gridView); 
     } 

     public override void LayoutSubviews() 
     { 
      base.LayoutSubviews(); 

      // determine the size of the basic frame in the scrolling area 
      CGRect pageFrame = SharedStatic.ScrollView.Frame; 
      // set it's overall size to n times the width, where is number pages, in this case 2 
      SharedStatic.ScrollView.ContentSize = new CGSize(pageFrame.Width * 2, pageFrame.Height); 

      _arenaView.Frame = pageFrame; 
      _arenaView.Image = UIImage.FromFile("someimage.jpg"); 

      // offset by the width to get to second page 
      pageFrame.X += SharedStatic.ScrollView.Frame.Width; 

      _gridView.Frame = pageFrame; 

      this.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints(); 
      this.AddConstraints 
      (
       _arenaView.WithSameTop(this), 
       _arenaView.WithSameBottom(this), 
       _arenaView.WithSameHeight(this), 
       _arenaView.WithSameWidth(this), 
       _gridView.Left().EqualTo().RightOf(_arenaView), 
       _gridView.WithSameTop(_arenaView), 
       _gridView.WithSameBottom(_arenaView), 
       _gridView.WithSameWidth(_arenaView) 
      ); 
     } 

Причина такого подхода, как я понимаю это, потому что не до раскладка различных подвидов бывает иметь были определены различные размеры и размеры. Это означает, что мне приходится компоновать «динамические» подзоны для моего изображения и сетки данных сами по себе в макете супервизоров для надлежащего применения ограничений, в то же время все еще получая события.

Мой первоначальный подход, чтобы поместить его все в ViewDidLayoutSubViews контроллера корневого представления (viewDidLayoutSubViews), привел к неправильному представлению, получающему весь сенсорный ввод, или, что еще хуже, к неправильной регистрации делегатов событий вообще. В какой-то мере это все равно «не знаю, почему это работает сейчас, но кому я должен жаловаться?»

Остается вопрос о постоянных и повторных вызовах LayoutSubviews(). Каждая (даже частичная) прокрутка, касание действия, segue будет повторять этот метод снова и снова, требуется ли изменение макета или нет. Я нашел одно полезное учебное пособие, которое я быстро не могу найти, что объясняет, чтобы сохранить информацию о состоянии и свести к минимуму количество вызовов умным способом, и я буду экспериментировать с этим следующим.

Информация при вызове LayoutSubviews можно найти здесь:

When is the layoutSubviews method called?

и ссылки в этой теме.

Жесткий бит жесткой связи через kludgy SharedStatic класс также будет заменен, но вы можете счесть полезным использовать такую ​​конструкцию на этапе тестирования и отладки.

Надеюсь, что это поможет кому-то!

Смежные вопросы