Як я можу прокрутити всі підпрограми UIView, а також їхні підпрограми та їхні підпрограми?
Як я можу прокрутити всі підпрограми UIView, а також їхні підпрограми та їхні підпрограми?
Відповіді:
Використовуйте рекурсію:
// UIView+HierarchyLogging.h
@interface UIView (ViewHierarchyLogging)
- (void)logViewHierarchy;
@end
// UIView+HierarchyLogging.m
@implementation UIView (ViewHierarchyLogging)
- (void)logViewHierarchy
{
NSLog(@"%@", self);
for (UIView *subview in self.subviews)
{
[subview logViewHierarchy];
}
}
@end
// In your implementation
[myView logViewHierarchy];
Ну ось моє рішення з використанням рекурсії та обгортки (категорії / розширення) для класу UIView.
// UIView+viewRecursion.h
@interface UIView (viewRecursion)
- (NSMutableArray*) allSubViews;
@end
// UIView+viewRecursion.m
@implementation UIView (viewRecursion)
- (NSMutableArray*)allSubViews
{
NSMutableArray *arr=[[[NSMutableArray alloc] init] autorelease];
[arr addObject:self];
for (UIView *subview in self.subviews)
{
[arr addObjectsFromArray:(NSArray*)[subview allSubViews]];
}
return arr;
}
@end
Використання: Тепер вам слід переглядати всі допоміжні подання та маніпулювати ними за потреби.
//disable all text fields
for(UIView *v in [self.view allSubViews])
{
if([v isKindOfClass:[UITextField class]])
{
((UITextField*)v).enabled=NO;
}
}
allSubViews
функція має витік пам'яті : ви повинні створити масив як [[[NSMutableArray alloc] init] autorelease]
або як [NSMutableArray array]
(що однаково).
Ось ще одна реалізація Swift:
extension UIView {
var allSubviews: [UIView] {
return self.subviews.flatMap { [$0] + $0.allSubviews }
}
}
Рішення в Swift 3, яке дає все, subviews
не включаючи сам вигляд:
extension UIView {
var allSubViews : [UIView] {
var array = [self.subviews].flatMap {$0}
array.forEach { array.append(contentsOf: $0.allSubViews) }
return array
}
}
nil
елементи з масиву, для безпеки під час виклику allSubViews
в підпроглядах.
Я позначаю все, коли воно створюється. Тоді легко знайти будь-яку підпрограму.
view = [aView viewWithTag:tag];
Щойно знайшов цікавий спосіб зробити це за допомогою налагоджувача:
http://idevrecipes.com/2011/02/10/exploring-iphone-view-hierarchies/
посилається на цей Apple Technote:
https://developer.apple.com/library/content/technotes/tn2239/_index.html#SECUIKIT
Просто переконайтеся, що ваш налагоджувач призупинено (або встановіть точку зупинки, щоб призупинити його вручну), і ви можете попросити recursiveDescription
.
Ось приклад із фактичною функцією циклу та розриву вигляду.
Стрімкий:
extension UIView {
func loopViewHierarchy(block: (_ view: UIView, _ stop: inout Bool) -> ()) {
var stop = false
block(self, &stop)
if !stop {
self.subviews.forEach { $0.loopViewHierarchy(block: block) }
}
}
}
Приклад дзвінка:
mainView.loopViewHierarchy { (view, stop) in
if view is UIButton {
/// use the view
stop = true
}
}
Зворотний цикл:
extension UIView {
func loopViewHierarchyReversed(block: (_ view: UIView, _ stop: inout Bool) -> ()) {
for i in stride(from: self.highestViewLevel(view: self), through: 1, by: -1) {
let stop = self.loopView(view: self, level: i, block: block)
if stop {
break
}
}
}
private func loopView(view: UIView, level: Int, block: (_ view: UIView, _ stop: inout Bool) -> ()) -> Bool {
if level == 1 {
var stop = false
block(view, &stop)
return stop
} else if level > 1 {
for subview in view.subviews.reversed() {
let stop = self.loopView(view: subview, level: level - 1, block: block)
if stop {
return stop
}
}
}
return false
}
private func highestViewLevel(view: UIView) -> Int {
var highestLevelForView = 0
for subview in view.subviews.reversed() {
let highestLevelForSubview = self.highestViewLevel(view: subview)
highestLevelForView = max(highestLevelForView, highestLevelForSubview)
}
return highestLevelForView + 1
}
}
Приклад дзвінка:
mainView.loopViewHierarchyReversed { (view, stop) in
if view is UIButton {
/// use the view
stop = true
}
}
Завдання-C:
typedef void(^ViewBlock)(UIView* view, BOOL* stop);
@interface UIView (ViewExtensions)
-(void) loopViewHierarchy:(ViewBlock) block;
@end
@implementation UIView (ViewExtensions)
-(void) loopViewHierarchy:(ViewBlock) block {
BOOL stop = NO;
if (block) {
block(self, &stop);
}
if (!stop) {
for (UIView* subview in self.subviews) {
[subview loopViewHierarchy:block];
}
}
}
@end
Приклад дзвінка:
[mainView loopViewHierarchy:^(UIView* view, BOOL* stop) {
if ([view isKindOfClass:[UIButton class]]) {
/// use the view
*stop = YES;
}
}];
За допомогою Оле Бегемана. Я додав кілька рядків, щоб включити в нього концепцію блоку.
UIView + HierarchyLogging.h
typedef void (^ViewActionBlock_t)(UIView *);
@interface UIView (UIView_HierarchyLogging)
- (void)logViewHierarchy: (ViewActionBlock_t)viewAction;
@end
UIView + HierarchyLogging.m
@implementation UIView (UIView_HierarchyLogging)
- (void)logViewHierarchy: (ViewActionBlock_t)viewAction {
//view action block - freedom to the caller
viewAction(self);
for (UIView *subview in self.subviews) {
[subview logViewHierarchy:viewAction];
}
}
@end
Використання категорії HierarchyLogging у вашому ViewController. Тепер ви маєте свободу у тому, що вам потрібно робити.
void (^ViewActionBlock)(UIView *) = ^(UIView *view) {
if ([view isKindOfClass:[UIButton class]]) {
NSLog(@"%@", view);
}
};
[self.view logViewHierarchy: ViewActionBlock];
Не потрібно створювати будь-яку нову функцію. Просто робіть це під час налагодження за допомогою Xcode.
Встановіть точку зупинки в контролері перегляду та зробіть програму паузою в цій точці зупинки.
Клацніть правою кнопкою миші порожню область та натисніть "Додати вираз ..." у вікні спостереження Xcode.
Введіть цей рядок:
(NSString*)[self->_view recursiveDescription]
Якщо значення занадто довге, клацніть правою кнопкою миші та виберіть "Друк Опис ...". Ви побачите всі підпрограми self.view у вікні консолі. Змініть self -> _ view на щось інше, якщо ви не хочете бачити підпрограми self.view.
Готово! Ні ГДБ!
Ось рекурсивний код: -
for (UIView *subViews in yourView.subviews) {
[self removSubviews:subViews];
}
-(void)removSubviews:(UIView *)subView
{
if (subView.subviews.count>0) {
for (UIView *subViews in subView.subviews) {
[self removSubviews:subViews];
}
}
else
{
NSLog(@"%i",subView.subviews.count);
[subView removeFromSuperview];
}
}
До речі, я створив проект з відкритим кодом, щоб допомогти у виконанні такого роду завдань. Це дуже просто, і використовує блоки Objective-C 2.0 для виконання коду на всіх представленнях в ієрархії.
https://github.com/egold/UIViewRecursion
Приклад:
-(void)makeAllSubviewsGreen
{
[self.view runBlockOnAllSubviews:^(UIView *view) {
view.backgroundColor = [UIColor greenColor];
}];
}
Ось варіація відповіді Оле Бегемана вище, яка додає відступ для ілюстрації ієрархії:
// UIView+HierarchyLogging.h
@interface UIView (ViewHierarchyLogging)
- (void)logViewHierarchy:(NSString *)whiteSpaces;
@end
// UIView+HierarchyLogging.m
@implementation UIView (ViewHierarchyLogging)
- (void)logViewHierarchy:(NSString *)whiteSpaces {
if (whiteSpaces == nil) {
whiteSpaces = [NSString string];
}
NSLog(@"%@%@", whiteSpaces, self);
NSString *adjustedWhiteSpaces = [whiteSpaces stringByAppendingFormat:@" "];
for (UIView *subview in self.subviews) {
[subview logViewHierarchy:adjustedWhiteSpaces];
}
}
@end
Код, розміщений у цій відповіді, охоплює всі вікна та всі подання та всі їхні підпрограми. Він використовувався для скидання роздруківки ієрархії подання до NSLog, але ви можете використовувати його як основу для будь-якого обходу ієрархії подання. Він використовує рекурсивну функцію C для обходу дерева перегляду.
Хотілося б, щоб я спочатку знайшов цю сторінку , але якщо (з якихось причин) ви хочете зробити це нерекурсивно, не в категорії, а з більшою кількістю рядків коду
Я думаю, що всі відповіді з використанням рекурсії (крім опції налагоджувача) використовували категорії. Якщо вам не потрібна / потрібна категорія, ви можете просто скористатися методом екземпляра. Наприклад, якщо вам потрібно отримати масив усіх міток у вашій ієрархії подання, ви можете це зробити.
@interface MyViewController ()
@property (nonatomic, retain) NSMutableArray* labelsArray;
@end
@implementation MyViewController
- (void)recursiveFindLabelsInView:(UIView*)inView
{
for (UIView *view in inView.subviews)
{
if([view isKindOfClass:[UILabel class]])
[self.labelsArray addObject: view];
else
[self recursiveFindLabelsInView:view];
}
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.labelsArray = [[NSMutableArray alloc] init];
[self recursiveFindLabelsInView:self.view];
for (UILabel *lbl in self.labelsArray)
{
//Do something with labels
}
}
Метод, наведений нижче, створює один або кілька змінних масивів, а потім циклічно переглядає підзаписи вхідного подання. Роблячи це, він додає початковий підпрогляд, а потім запитує, чи є в цьому підпрогляді будь-які підпрограми. Якщо це правда, вона знову називає себе. Він робить це доти, доки не буде додано всі погляди ієрархії.
-(NSArray *)allSubs:(UIView *)view {
NSMutableArray * ma = [NSMutableArray new];
for (UIView * sub in view.subviews){
[ma addObject:sub];
if (sub.subviews){
[ma addObjectsFromArray:[self allSubs:sub]];
}
}
return ma;
}
Телефонуйте за допомогою:
NSArray * subviews = [self allSubs:someView];