Привет всем!

История началась с того, что мне понадобилось написать приложение под Mac OS X. Все реже можно увидеть статьи по поводу разработок под desktop, да еще и под Mac OS X, но эта тема именно об этом.

Я уже писал много чего на QT, а тут встала задача использовать исключительно Cocoa.

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

Вот в проекте я дошел до разработки пользовательского интерфейса и тут мне понадобился классический шаблон Tab View. Продолжение читайте уже под катом.



Предыстория


В общем проект самый обычный в плане пользовательского интерфейса. Пользователь кликает кнопку connect, вводит данные для входа на сервер жмет OK и появляется вкладка для этого сервера и т.д. Войти можно на много серверов и они будут в разных вкладках. Что за сервера и зачем это все нужно это уже другая предметная область не относящаяся к данной статье, поэтому эту информацию я может быть расскажу в другой раз.

Наивный разработчик


Под Mac OS X, да и даже под iOS я еще ни разу не разрабатывал, это мой первый опыт, возможно именно это меня и сделало еще более наивным.
Я думал что тут все проще простого, кину в конструкторе Tab View или как он будет называться в Cocoa в окно, настрою отображение и все, но не тут-то было.

Нахожу в Xcode tab view, кидаю в окно и вижу вот это


Тут уже у меня появились подозрения, что могут возникнуть какие-то сложности.

Ладно подумал я, начал искать другие виджеты для вкладок, их нет. Хорошо, решил разобраться с настройками этого Tab View. Минут 10 повозившись с настройками и поняв, что Tab View к виду вкладок в том же Safari/Finder/Xcode и прочих, его не привести, я отправился на поиски по безграничным просторам интернета.

За первые несколько просмотров постов на http://stackoverflow.com стало ясно, что Tab View это совсем не то что нужно, я не одинок.

Поиск альтернативного решения


Тут наступил такой момент в разработке когда не пишется ни строчки кода зато читается очень много текста и выпивается очень много кофе.
Вначале я всё-таки попробовал найти что-то от самих Apple, но очень быстро стало ясно что это бесполезно.
Далее я отправился на различные форумы где обсуждалась эта проблема и пути ее решения. Довольно быстро я нашел уже несколько вариантов добавления вкладок в приложения под Mac OS X.



Первые два варианта были самыми распространенными и с большим количеством функционала и я решил разбираться с ними.
PSMTabBarControl достачно развитый проект с хорошим функционалом, но уже давно не поддерживается. К слову именно он используется в Adium.
MMTabBarView по сути является продолжением и развитием PSMTabBarControl, но не смотря на это, с поддержкой у его не лучше.
Если взглянуть на его страницу на github, то у него имеется 44 форка, а все это потому что разработчик уже год как не коммитил ничего в master ветку хотя и делал некоторые доработки в develop(но это судя по всему мало кто замечает).
44 форка и среди них несколько активных уходящих от последнего коммита в master и утверждающих что они исправили какие-то баги.
Тут я решил взять тот который активнее других поддерживается.

Был выбран один из форков (как оказалось в последствии совсем не важно какой).

В MMTabBarView есть демо проект который без проблем был откомпилен и работал:



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

Тупик


Табов нет, ладно, я начал разбираться и выяснил что что-то похожее на табы появляется если я не обновляю Tab View



Если например добавить таб то все пропадает



Вот тут началось веселье. Я начал пробовать все что можно, сначала все разумные средства, а потом уже и не разумные.

  • Разные форки. Пришлось перебрать порядка 3-4 форков.
  • Настройки. Пробовал разные настройки.
  • Я делал новый проект и пробовал все в нем с разными форками.
  • На github и в поиске я нашел проекты использующие MMTabBarView и разбирался с ними. Искал отличия с моим кодом.
  • Перекапывал форумы в интернете на эту тему.

В общем на многочисленные эксперименты ушел не один день.

В конце у меня было 2 проекта, практически пустых и полностью одинаковых. В одном табы работают, а в другом нет. Встречаясь с таким необъяснимым, наши предки начинали верить в сверхъестественное.

Начали возникать мысли что нужно поискать другую библиотеку.

Эврика или скорее удача


В итоге после долгих поисков непонятной проблемы я решил найти на github максимально простой и маленький проект использующий MMTabBarView и сравнить его с моим.
Был найден какой-то маленький браузер. Из него я убрал все лишнее, и начал сравнивать со своим проектом. Все было идентично. Он так же без проблем работал со сборками MMTabBarView из разных форков.
Дальше возникла идея в этом работающем проекте сделать новое окно и попробовать в нем подключить табы.
И тут победа, табы не заработали. И вот у меня один проект, с одним xib-файлом табы работают с другим нет.
Вывод был очевиден, надо сравнить эти файлы.

Тут работает:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6751" systemVersion="13F1603" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" promptedForUpgradeToXcode5="NO">


Тут не работает:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6751" systemVersion="13F1603" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">


В первом же теге я нашел проблему:
customObjectInstantitationMethod="direct"

Если убрать этот атрибут то MMTabBarView начинает корректно работать.

Видимо это связано с тем что отрисовка MMTabBarView ведется как раз в CustomView и этот атрибут как-то на него влияет.

Начинаем разбираться что это такое:

Гугл и яндекс дают кучу ссылок на проиндексированные xib файлы.

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

Заключение


MMTabBarView досточно хороший проект, он поддерживает разное отображение табов, счетчик на табах, добавление иконок, добавление кнопок закрытия и добавление нового таба и многое другое. Жаль только что он размазался по куче форков и непонятно на данный момент как его собрать воедино.
Apple конечно меня огорчил, ну как такое может быть, во всех приложениях элемент пользовательского интерфейса есть, а в библиотеке его нет!?
Самое забавное что табы есть в xcode :)

PS
Вот маленький пример, как подключить MMTabBarView к вашему проекту.

Полезные ссылки:
stackoverflow.com/questions/27354262/safari-style-tabs-cocoa-control?rq=1
stackoverflow.com/questions/2774668/does-anyone-know-a-safari-style-tab-control-for-mac-os-x-applications?rq=1
stackoverflow.com/questions/22789341/better-psmtabbarcontrol-cocoa-tabs
stackoverflow.com/questions/4986920/tabs-style-in-the-cocoa?rq=1

Комментарии (11)


  1. Krypt
    28.02.2016 23:11
    +1

    Вы стали жертвой непонимания терминов.
    Табы в понимании Mac OS X — это именно то, что вы увидели.
    Вкладки сафари — стандартным элементом системы, вроде бы как, не являются.

    А вообще iOS в плане API по сравнению в OS X выглядит как большая работа над ошибками.


    1. xanm
      28.02.2016 23:15

      Ну тут еще сыграло то что я привык к QTabWidget, но не спорю что-то в Cocoa я новичек.


  1. kafeman
    29.02.2016 00:00

    Забавно, что в упомянутом Qt такое есть. Правда, начиная с 10.10 работает как-то криво.

    Что касается ваших скриншотов, табы в том же Finder выглядят немного иначе — нет крестика и ширина растягивается на все окно.


    1. xanm
      29.02.2016 07:32

      Да, в QT чего только нету.

      Что касается табов то в MMTabBarView — есть стиль Safari который уже ближе к Finder, да и можно написать свой, есть опция setOnlyShowCloseOnHover, растягивание таба на я не нашел, но добавить его будет не проблема.


  1. Athari
    29.02.2016 10:03
    +2

    Когда вы пишете "Какие-то примеры можно найти на сайте cocoacontrols.com" или "Что-то предлагалось на stackoverflow.com", то я ожидаю, что ссылки ведут на "как-то примеры" и "что-то", а никак не на главную страницу сайтов. Вы два раза сослались на обсуждения на СО, и оба раза дали ссылку на главную страницу. Если уж даёте ссылки, до делайте их полезными, а не для галочки. Вы бы ещё написали "нашёл в Google" и дали ссылку на главную страницу поисковика.


    1. xanm
      29.02.2016 11:03
      +3

      На самом деле это просто редактор хабра преобразует их в ссылки, что же касается других примеров, добавил. Можете посмотреть :)


  1. silvansky
    29.02.2016 11:34

    Да, помню, как брали PSMTabBarControl и допиливали до какого-то адекватного состояния…
    Кстати, а MMTabBarView нормально дружит с Autolayout?


    1. xanm
      29.02.2016 11:38

      Пока проблем вроде не возникало, но кто знает что еще может вылезти.


  1. aspcartman
    29.02.2016 11:43
    +1

    Добро пожаловать на борт.
    Не используйте InterfaceBuilder. Он принесет больше боли, чем пользы. На всех платформах боль от использования средств создания интерфейса графическим путем примерно одинакова, но в случае ios/osx интерфейс эпически просто создавать из кода благодаря оберткам над AutoLayout (использовать его в чистом виде это как пить чистый спирт, не надо).

    https://github.com/Tricertops/KeepLayout

    Добавление Вашего таб бара бы выглядело в методе -loadView следующим образом:

    ```
    MMTabBarView *tabBar = [MMTabBarView new];
    tabBar.foo = bar; // тут выставляем всякие параметры
    [view addSubview:tabBar];
    tabBar.keepTopInset.equal = 0;
    tabBar.keepHorizontalInsets.equal = 0;
    tabBar.keepHeight.equal = 20; // Опционально, если автор не указал стандартный размер через -intrinsicContentSize
    _tabBarIvar = tabBar;
    ```

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

    И еще: AppCode, Cocoapods.

    Happy coding! ^_^


    1. obyknovenius
      29.02.2016 13:14
      +2

      Почему все так хейтят InterfaceBuilder? Вас никто не заставляет в нем все делать. Это еще один инструмент. И иногда в нем быстрее и удобнее что-то сделать, чем написать дофига строк кода. Например, когда дизайн до конца не утвержден и заказчик хочет посмотреть как это будет на девайся, потыкать пальцами, а потом половину переделать. И здесь IB и Storyboard'ы очень даже помогают.

      AutoLayout даже в коде я использую безо всяких оберток (может потому что и спирт чистый пил когда-то давно). Я вообще не очень люблю тянуть кучу всего стороннего в проект. Не хочу зависеть от компонентов, которые написали непроверенные люди и неизвестно собираются ли они свои велосипеды поддерживать.

      И еще: не использую AppCode, не использую Cocoapods, хотя пробовал и умею пользоваться и тем и другим.

      Вообще, почему-то в среде iOS разработки популярно мнение: Apple сделало что-то (CoreData, AutoLayout, подключаемые Frameworks) слишком сложно, не хочу разбираться, скачаю что-нибудь попроще. А потом получается — а зачем мне использовать NSURLSession или ту же CoreData, если я уже умею AFNetworking и MagicalRecord, например.


      1. aspcartman
        29.02.2016 13:24

        Все хейтят IB потому что люди не умеют его готовить и строят громадные приложения в одной сториборде, а потом за ними переписывай.

        И я заказчику накодю что угодно, и даже не буду ждать перекомпиляции проекта — хоткей и в симуляторе интерфейс перестроился. Если писать без обертки на чистом AL, то это действительно дольше, чем в IB.

        AL без оберток очень плохо читаем.
        iOS разработка != java dev != c++ dev. Тут другие правила и по факту 50 зависимостей в cocoapods гораздо лучше, чем свои решения и на практике проблем с ними не возникает. Есть непоправленная бага в стороннем проекте — форкни, поправь, в поде выставь ссылку на свой git, profit. Pull request не забыть сделать. Это гораздо быстрее, чем делать что либо самому. Потом на ваше место прийдет другой разработчик, и ему в этом всем разбираться.

        А собственно ответьте на свой вопрос сами: а зачем? Чтобы понимать? Ясен перец нужно, а еще?