Як використовувати автоматичну компоновку для переміщення інших поглядів, коли подання приховано?


333

Я створив власну клітинку в IB, підкласифікував її і підключив свої торгові точки до мого користувацького класу. У мене є три підперегляди вмісту комірок: UIView (cdView) та дві мітки (titleLabel та emailLabel). Залежно від даних, доступних для кожного рядка, іноді мені хочеться, щоб у моїй комірці відображалися UIView та дві мітки, а іноді лише дві мітки. Що я намагаюся зробити, це встановити обмеження таким чином, якщо я встановлю властивість UIView прихованим або я вилучу його з нагляду, дві мітки перемістяться зліва. Я спробував встановити провідне обмеження UIView на Superview (вміст комірки) для 10px, а UILabels, що ведуть обмеження на 10 px, до наступного перегляду (UIView). Пізніше в моєму коді

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(IndexPath *)indexPath {
...
Record *record = [self.records objectAtIndex:indexPath.row];

if ([record.imageURL is equalToString:@""]) {
     cell.cdView.hidden = YES;
}

Я ховаю свій cell.cdView, і я хотів би, щоб мітки переміщалися вліво, однак вони залишаються на тому самому місці в Cell. Я спробував видалити Cell.cdView з нагляду, але це також не вийшло. Я додав зображення, щоб уточнити, про що я.

клітина

Я знаю, як це зробити програмно, і не шукаю цього рішення. Те, що я хочу, - це встановити обмеження в ІБ, і я очікую, що мої підгляди будуть рухатися динамічно, якщо інші види видаляються чи приховуються. Чи можливо це зробити в ІБ з автоматичним розташуванням?

.....

Змініть тривалість виконання обмежень - перевірте цю відповідь
Кампай

Для цього конкретного випадку ви також можете використовувати UIStackView. коли ви приховуєте компакт-диск, мітки займуть свій простір
Marco Pappalardo,

Відповіді:


373

Це можливо, але вам доведеться зробити трохи додаткової роботи. Є кілька концептуальних речей, щоб першими вийти з шляху:

  • Приховані подання, хоча вони і не малюють, все ж беруть участь у автоматичному макеті і зазвичай зберігають свої кадри , залишаючи інші пов’язані перегляди на своїх місцях.
  • При видаленні подання з його огляду всі пов’язані обмеження також видаляються з цієї ієрархії подання.

У вашому випадку це, ймовірно, означає:

  • Якщо встановити лівий вигляд прихованим, мітки залишаються на місці, оскільки цей лівий вигляд все ще займає місце (навіть якщо його не видно).
  • Якщо ви видалите лівий вигляд, ваші мітки, ймовірно, залишаться неоднозначно обмеженими, оскільки у вас більше немає обмежень для лівих країв вашої мітки.

Те, що вам потрібно зробити, - це розумно надмірно обмежувати ваші етикетки. Залиште існуючі обмеження (10 крапків місця для іншого виду) у спокої, але додайте ще одне обмеження: зробіть ліві краї ваших міток на 10 кроків від лівого краю їхнього огляду з необхідним пріоритетом (високий пріоритет за замовчуванням, ймовірно, спрацює добре).

Потім, коли ви хочете, щоб вони рухалися ліворуч, повністю видаліть подання зліва. Обов'язкове обмеження на 10 крат до лівого подання зникне разом із поданням, до якого воно стосується, і вам залишиться лише обмеження з високим пріоритетом, щоб мітки знаходилися на відстані 10 крап від їхнього огляду. Під час наступного пропуску макета це повинно змусити їх розширюватися ліворуч до тих пір, поки вони не заповнять ширину огляду, але для вашого відстані по краях.

Одне важливе застереження: якщо ви хочете, щоб ваш лівий погляд повернувся на малюнок, вам не тільки потрібно додати його назад до ієрархії перегляду, але також вам доведеться одночасно встановити всі його обмеження . Це означає, що вам потрібен спосіб повернути обмеження на відстань 10pt між поданням та його мітками щоразу, коли цей вид буде показаний знову.


8
Хоча ця відповідь, безумовно, спрацює, IMO, надмірне обмеження для обробки різних випадків використання, здається, є кодовим запахом - тим більше, що вам доведеться відновити всі обмеження для будь-яких видалених поглядів, які ви хочете знову переглянути.
пам’ятки

На мою думку, це не шлях. Натомість слід використовувати обмеження ширини / висоти для подання, яке ви хочете приховати.
ullstrm

26
Я з повагою не згоден. Якщо ви (наприклад) встановите ширину подання на 0, у вас виникнуть дві проблеми. По-перше, тепер у вас подвійний проміжок між оглядом і видимим видом: |-(space)-[hidden(0)]-(space)-[visible]ефективно |-(2*space)-[visible]. По-друге, цей погляд може почати викидати порушення обмежень залежно від його власного піддерева та обмежень перегляду - ви не можете гарантувати, що ви можете довільно обмежувати подання на ширину 0 і змушувати його працювати.
Тім

1
Якщо ви використовуєте подання з властивим розміром вмісту, відповідь Тіма видається унікальним способом.
балкот

Дякую Тим. Я встановлюю обмеження 0 з більшим пріоритетом, щоб уникнути несумісності обмежень, але тепер я розумію, що існує проблема з подвійним інтервалом. У мене не було цієї проблеми, тому що я ніколи не показую два погляди разом (мій випадок |-[otherViews]-[eitherThis][orThis]-|:), але я врешті-решт зіткнувся з цією проблемою.
Ферран Мейлінч

207

Додавання та усунення обмежень під час виконання - це важка операція, яка може вплинути на продуктивність. Однак є простіша альтернатива.

Для подання, яке ви хочете приховати, встановіть обмеження по ширині. Обмежте інші погляди провідним горизонтальним зазором до цього виду.

Щоб приховати, оновіть .constantобмеження ширини до 0.f. Інші представлення автоматично перемістяться вліво, щоб прийняти позицію.

Дивіться мою іншу відповідь тут для більш детальної інформації:

Як змінити обмеження міток під час виконання?


29
Єдина проблема цього рішення полягає в тому, що поле зліва буде подвоєне того, що ви, мабуть, хочете, тому я б також оновив одне з цих обмежень, але навіть тоді я думаю, що це менше роботи, ніж видалення підвиду.
Жозе Мануель Санчес

2
@ skinsfan00atg Якщо ви використовуєте перегляд із внутрішнім розміром вмісту, ви не можете використовувати це рішення.
балкот

@balkoth чому б і ні? Ви можете просто зменшити пріоритет захоплення вмісту
Макс Маклеод

2
@MaxMacLeod Якщо ви зменшите пріоритет приховання вмісту, ви не використовуєте внутрішній розмір вмісту, ви використовуєте розмір, вказаний обмеженням.
балкот

2
@MaxMacLeod Добре, я зрозумів, що ти маєш на увазі. Потрібно встановити пріоритет стійкості до стиснення до 0 (не обіймати вміст) у коді, коли ви хочете приховати перегляд та коли ви хочете знову показати це відновити це значення. Крім цього вам потрібно додати обмеження в конструкторі інтерфейсів, щоб встановити розмір представлення на 0. Не потрібно торкатися цього контракту в коді.
балкот

87

Для тих , хто підтримує IOS 8+ тільки, є нове Логічне властивість активна . Це допоможе динамічно включити лише необхідні обмеження

Розетка обмеження PS повинна бути сильною , а не слабкою

Приклад:

@IBOutlet weak var optionalView: UIView!
@IBOutlet var viewIsVisibleConstraint: NSLayoutConstraint!
@IBOutlet var viewIsHiddenConstraint: NSLayoutConstraint!

func showView() {
    optionalView.isHidden = false
    viewIsVisibleConstraint.isActive = true
    viewIsHiddenConstraint.isActive = false
}

func hideView() {
    optionalView.isHidden = true
    viewIsVisibleConstraint.isActive = false
    viewIsHiddenConstraint.isActive = true
}

Крім того, щоб виправити помилку в раскадровці, вам потрібно зняти Installedпрапорець для одного з цих обмежень.

UIStackView (iOS 9+)
Ще одним варіантом є перегляд своїх поглядів UIStackView. Після того, як вигляд буде прихований UIStackView, оновлення планується автоматично


активно частково працює. Але якщо я використовую його в комірці для багаторазового використання, від активації до деактивації, працює, але не для деактивації, щоб активувати. Будь-які ідеї? Або ви могли б навести приклад?
Aoke Li

10
Це трапляється тому, що деактивоване обмеження не завантажується і не розміщується. Ваша розмежувальна розетка повинна бути сильною, а не слабкою
Сильмаріл

Де ми можемо знайти цю "активну" власність?
Лакшмі Редді


Мабуть, найвибагливішою відповіддю може виявитися надмірне обмеження + встановлення обмежень.
Ting Yi Shih

68

UIStackViewрепозиціонує свої представлення автоматично, коли hiddenвластивість буде змінено на будь-якому з його переглядів (iOS 9+)

UIView.animateWithDuration(1.0) { () -> Void in
   self.mySubview.hidden = !self.mySubview.hidden
}

Перейти до 11:48 у цьому відеозаписі WWDC для демонстрації:

Таємниці автоматичного макета, частина 1


Я сховав вкладений вигляд стека, і весь вигляд стека зник.
Ян Варбуртон

5
Це має бути прийнятою відповіддю. Дотримуйтесь яблучних рекомендацій wwdc 2015 щодо дизайну інтерфейсу інтерфейсу.
thibaut noah

@thibautnoah А як тоді підтримка iOS 8-?
багаж

5
@bagage Якщо ви не є facebook або Google, ви можете кинути його. З охопленням від iOS 9 ви будете підтримувати більше 90% пристроїв, що більш ніж достатньо. Підтримка нижче калічить процес розробки і перешкодити вам використовувати новітні функції ІМО
Тібо Ной

16

Мій проект використовує спеціальний @IBDesignableпідклас UILabel(щоб забезпечити послідовність у кольорі, шрифті, вставках тощо), і я реалізував щось на зразок наступного:

override func intrinsicContentSize() -> CGSize {
    if hidden {
        return CGSizeZero
    } else {
        return super.intrinsicContentSize()
    }
}

Це дозволяє підкласу мітки брати участь в автоматичному макеті, але не займає місця, коли він прихований.


13

Для Googlers: спираючись на відповідь Макса, щоб вирішити питання прокладки, яке багато хто помітив, я просто збільшив висоту етикетки і використав цю висоту як роздільник, а не фактичне набивання. Цю ідею можна розширити для будь-якого сценарію, що містить перегляди.

Ось простий приклад:

Скріншот IB

У цьому випадку я зіставляю висоту мітки Автор відповідному IBOutlet:

@property (retain, nonatomic) IBOutlet NSLayoutConstraint* authorLabelHeight;

і коли я встановлюю висоту обмеження 0.0f, ми зберігаємо "підкладку", оскільки висота кнопки " Відтворення " дозволяє це.


Для тих, хто новачок, NSLayoutConstraintя вважаю, що ви хочете оновити constantвласність свого authorLabelHeight.
Кайл

8

підключити обмеження між uiview та мітками як IBOutlet та встановити члена пріоритету на менше значення, коли встановлено приховано = ТАК


3
Ви не можете налаштувати пріоритет NSLayoutConstraint після його встановлення; ви повинні видалити та прочитати нове обмеження з іншим пріоритетом.
Тім

7
Я змусив цей метод працювати. У мене є випадок, який використовує мітку та кнопку, де мені потрібно приховати кнопку та розширити ярлик. У мене є два обмеження: одне спочатку з пріоритетом 751, а інше - 750. Потім, коли я приховую кнопку, я гортаю пріоритети, і довжина мітки зростає. Слід зауважити, що якщо ви намагаєтеся зробити більш високий пріоритет 1000, ви отримуєте помилку "Не змінюється пріоритет із необхідного на не встановлене обмеження (або навпаки)." Так що ні, і вам здається, що все добре. Xcode 5.1 / viewDidLoad.
Джон Бушнелл

8

Що я в кінцевому підсумку робив, це створити 2 xibs. Один з видом зліва і один без нього. Я зареєстрував обидва в контролері, а потім вирішив, який використовувати під час cellForRowAtIndexPath.

Вони використовують той же клас UITableViewCell. Мінусом є те, що між xibs є деяке дублювання вмісту, але ці клітини є досить основними. Переваги полягають у тому, що у мене немає купи коду, щоб вручну керувати видаленням представлення даних, оновленням обмежень тощо.

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

[self.table registerNib:[UINib nibWithNibName:@"TrackCell" bundle:nil] forCellReuseIdentifier:@"TrackCell"];
[self.table registerNib:[UINib nibWithNibName:@"TrackCellNoImage" bundle:nil] forCellReuseIdentifier:@"TrackCellNoImage"];

TrackCell *cell = [tableView dequeueReusableCellWithIdentifier:(appDelegate.showImages ? @"TrackCell" : @"TrackCellNoImage") forIndexPath:indexPath];

7

У цьому випадку я зіставляю висоту мітки Author відповідному IBOutlet:

@property (retain, nonatomic) IBOutlet NSLayoutConstraint* authorLabelHeight;

і коли я встановив висоту обмеження на 0,0f, ми зберігаємо "підкладку", оскільки висота кнопки "Відтворення" дозволяє це.

cell.authorLabelHeight.constant = 0;

введіть тут опис зображення введіть тут опис зображення


6

Використовуйте два горизонтальних та вертикальних UIStackView, коли деякий вигляд підпогляду в стеці приховано, інші підгляди стека будуть переміщені, використовуйте розподіл -> Заповніть пропорційно для вертикального стека двома UILabels та для першого UIView потрібно встановити ширину та висоту.введіть тут опис зображення


2

Спробуйте це, я реалізував нижче код,

У мене є один погляд на ViewController, який додав три інші представлення. Коли будь-який вид прихований, інші два подання перемістяться, виконайте наведені нижче дії. ,

Файл 1.ViewController.h

#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@property (strong, nonatomic) IBOutlet UIView *viewOne;
@property (strong, nonatomic) IBOutlet UIView *viewTwo;
@property (strong, nonatomic) IBOutlet UIView *viewThree;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *viewOneWidth;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *viewTwoWidth;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *viewThreeWidth;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *viewBottomWidth;
@end

2.ViewController.m

 #import "ViewController.h"
 @interface ViewController ()
{
  CGFloat viewOneWidthConstant;
  CGFloat viewTwoWidthConstant;
  CGFloat viewThreeWidthConstant;
  CGFloat viewBottomWidthConstant;
}
@end

@implementation ViewController
@synthesize viewOne, viewTwo, viewThree;

- (void)viewDidLoad {
  [super viewDidLoad];
 // Do any additional setup after loading the view, typically from a 
  nib.

  /*
   0  0   0
   0  0   1
   0  1   0
   0  1   1
   1  0   0
   1  0   1
   1  1   0
   1  1   1
   */

  //    [viewOne setHidden:NO];
  //    [viewTwo setHidden:NO];
  //    [viewThree setHidden:NO];

  //    [viewOne setHidden:NO];
  //    [viewTwo setHidden:NO];
  //    [viewThree setHidden:YES];

  //    [viewOne setHidden:NO];
  //    [viewTwo setHidden:YES];
  //    [viewThree setHidden:NO];

  //    [viewOne setHidden:NO];
  //    [viewTwo setHidden:YES];
  //    [viewThree setHidden:YES];


  //    [viewOne setHidden:YES];
  //    [viewTwo setHidden:NO];
  //    [viewThree setHidden:NO];

  //    [viewOne setHidden:YES];
  //    [viewTwo setHidden:NO];
  //    [viewThree setHidden:YES];

 //    [viewOne setHidden:YES];
 //    [viewTwo setHidden:YES];
 //    [viewThree setHidden:NO];

//    [viewOne setHidden:YES];
//    [viewTwo setHidden:YES];
//    [viewThree setHidden:YES];

 [self hideShowBottomBar];
  }

- (void)hideShowBottomBar
{
  BOOL isOne = !viewOne.isHidden;
  BOOL isTwo = !viewTwo.isHidden;
  BOOL isThree = !viewThree.isHidden;

  viewOneWidthConstant = _viewOneWidth.constant;
  viewTwoWidthConstant = _viewTwoWidth.constant;
  viewThreeWidthConstant = _viewThreeWidth.constant;
  viewBottomWidthConstant = _viewBottomWidth.constant;

   if (isOne && isTwo && isThree) {
    // 0    0   0
    _viewOneWidth.constant = viewBottomWidthConstant / 3;
    _viewTwoWidth.constant = viewBottomWidthConstant / 3;
    _viewThreeWidth.constant = viewBottomWidthConstant / 3;
    }
    else if (isOne && isTwo && !isThree) {
     // 0    0   1
    _viewOneWidth.constant = viewBottomWidthConstant / 2;
    _viewTwoWidth.constant = viewBottomWidthConstant / 2;
    _viewThreeWidth.constant = 0;
    }
   else if (isOne && !isTwo && isThree) {
    // 0    1   0
    _viewOneWidth.constant = viewBottomWidthConstant / 2;
    _viewTwoWidth.constant = 0;
    _viewThreeWidth.constant = viewBottomWidthConstant / 2;
    }
    else if (isOne && !isTwo && !isThree) {
    // 0    1   1
    _viewOneWidth.constant = viewBottomWidthConstant;
    _viewTwoWidth.constant = 0;
    _viewThreeWidth.constant = 0;
   }
   else if (!isOne && isTwo && isThree) {
    // 1    0   0
    _viewOneWidth.constant = 0;
    _viewTwoWidth.constant = viewBottomWidthConstant / 2;
    _viewThreeWidth.constant = viewBottomWidthConstant / 2;
   }
   else if (!isOne && isTwo && !isThree) {
    // 1    0   1
    _viewOneWidth.constant = 0;
    _viewTwoWidth.constant = viewBottomWidthConstant;
    _viewThreeWidth.constant = 0;
   }
   else if (!isOne && !isTwo && isThree) {
    // 1    1   0
    _viewOneWidth.constant = 0;
    _viewTwoWidth.constant = 0;
    _viewThreeWidth.constant = viewBottomWidthConstant;
   }
   else if (isOne && isTwo && isThree) {
    // 1    1   1
    _viewOneWidth.constant = 0;
    _viewTwoWidth.constant = 0;
    _viewThreeWidth.constant = 0;
   }
  }

 - (void)didReceiveMemoryWarning {
  [super didReceiveMemoryWarning];
 // Dispose of any resources that can be recreated.
 }
 @end

введіть тут опис зображення введіть тут опис зображення введіть тут опис зображення

Сподіваюсь, що ця логіка комусь допоможе.


1

У моєму випадку я встановив постійну висоту обмеження на 0.0fа також встановити hiddenвластивість YES.

Щоб знову показати подання (з підзаглядом), я зробив протилежне: встановив постійну висоту на ненульове значення і встановив hiddenвластивість NO.


1

Я буду використовувати горизонтальний стеквью. Він може видалити кадр, коли підвід прихований.

На зображенні нижче, червоний вигляд є фактичним контейнером для вашого вмісту і має 10-кратний простір для помаранчевого перегляду (ShowHideView), а потім просто підключіть ShowHideView до IBOutlet та покажіть / приховає / видаліть його програмно.

  1. Це коли вигляд видно / встановлено.

вид видно

  1. Це коли вигляд приховано / не встановлено.

перегляд приховано / видалено


1

Це ще одне моє рішення з використанням пріоритетного обмеження. Ідеєю встановлюється ширина на 0.

  1. створити подання контейнера (помаранчевий) та встановити ширину. введіть тут опис зображення

  2. створити перегляд вмісту (червоний) та встановити пробіл 10pt для перегляду (помаранчевий). Зауважте обмеження проміжку простору, є 2 обмеження з різним пріоритетом. Низький (= 10) і Високий (<= 10). Це важливо, щоб уникнути неоднозначності. введіть тут опис зображення

  3. Встановіть помаранчевий вигляд на 0, щоб приховати його. введіть тут опис зображення


0

Як запропонував no_scene, ви точно можете це зробити, змінивши пріоритет обмеження під час виконання. Це було набагато простіше для мене, тому що у мене було більше одного блокувального перегляду, який потрібно було б видалити.

Ось фрагмент із використанням ReactiveCocoa:

RACSignal* isViewOneHiddenSignal = RACObserve(self.viewModel, isViewOneHidden);
RACSignal* isViewTwoHiddenSignal = RACObserve(self.viewModel, isViewTwoHidden);
RACSignal* isViewThreeHiddenSignal = RACObserve(self.viewModel, isViewThreeHidden);
RAC(self.viewOne, hidden) = isViewOneHiddenSignal;
RAC(self.viewTwo, hidden) = isViewTwoHiddenSignal;
RAC(self.viewThree, hidden) = isViewThreeHiddenSignal;

RAC(self.viewFourBottomConstraint, priority) = [[[[RACSignal
    combineLatest:@[isViewOneHiddenSignal,
                    isViewTwoHiddenSignal,
                    isViewThreeHiddenSignal]]
    and]
    distinctUntilChanged]
    map:^id(NSNumber* allAreHidden) {
        return [allAreHidden boolValue] ? @(780) : @(UILayoutPriorityDefaultHigh);
    }];

RACSignal* updateFramesSignal = [RACObserve(self.viewFourBottomConstraint, priority) distinctUntilChanged];
[updateFramesSignal
    subscribeNext:^(id x) {
        @strongify(self);
        [self.view setNeedsUpdateConstraints];
        [UIView animateWithDuration:0.3 animations:^{
            [self.view layoutIfNeeded];
        }];
    }];

0

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

AutolayoutHelper

Це може бути трохи адаптовано до моїх потреб, але ви можете вважати його корисним або, можливо, захочете його змінити та створити власного помічника.

Я повинен подякувати Тіму за його відповідь вище , цю відповідь про UIScrollView, а також цей підручник .


0

Ось як я повторно вирівняю свої uiviews, щоб отримати ваше рішення:

  1. Перетягніть один UIImageView і поставте його зліва.
  2. Перетягніть один UIView і помістіть його праворуч від UIImageView.
  3. Перетягніть два UILabels у цей UIView, провідні та кінцеві обмеження яких дорівнюють нулю.
  4. Встановіть провідне обмеження UIView, що містить 2 мітки, для перегляду замість UIImagView.
  5. ЯКЩО UIImageView приховано, встановіть провідну константу обмеження на 10 пікс для нагляду. ELSE, встановіть провідну константу обмеження на 10 px + UIImageView.width + 10 px.

Я створив власне правило великого пальця. Кожен раз, коли вам доведеться приховати / показати будь-який uiview, на обмеження якого можуть вплинути, додайте всі підпогляди, які впливають / залежать, всередині uiview та оновіть його постійне / кінцеве / верхнє / нижнє обмеження постійне програмно.


0

Це старе питання, але все ж, я сподіваюся, що це допоможе. Починаючи з Android, у цій платформі у вас є зручний метод isVisibleприховати його від подання, але також не матимуть кадр, який розглядається, коли автомашина малює подання.

використовуючи розширення та «розширити» uiview, ви можете виконати подібну функцію в ios (не впевнений, чому це вже не в UIKit), тут реалізація у швидкому 3:

    func isVisible(_ isVisible: Bool) {
        self.isHidden = !isVisible
        self.translatesAutoresizingMaskIntoConstraints = isVisible
        if isVisible { //if visible we remove the hight constraint 
            if let constraint = (self.constraints.filter{$0.firstAttribute == .height}.first){
                self.removeConstraint(constraint)
            }
        } else { //if not visible we add a constraint to force the view to have a hight set to 0
            let height = NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal , toItem: nil, attribute: .notAnAttribute, multiplier: 0, constant: 0)
            self.addConstraint(height)
        }
        self.layoutIfNeeded()
    }

0

правильний спосіб зробити це - відключити обмеження з isActive = false. проте зауважте, що деактивація обмеження видаляє та звільняє його, тому вам доведеться мати потужні розетки для них.


0

Найпростіше рішення - використовувати UIStackView (горизонтальний). Додати до подання стека: перший і другий подання з мітками. Потім встановіть властивість першого вигляду приховано на хибне. Усі обмеження будуть розраховані та оновлені автоматично.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.