Làm cách nào để tôi có thể xem qua tất cả các lượt xem phụ của một UIView và các lượt xem phụ cũng như lượt xem phụ của họ?
Làm cách nào để tôi có thể xem qua tất cả các lượt xem phụ của một UIView và các lượt xem phụ cũng như lượt xem phụ của họ?
Câu trả lời:
Sử dụng đệ quy:
// 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];
Đây là giải pháp của tôi bằng cách sử dụng đệ quy và trình bao bọc (danh mục / phần mở rộng) cho lớp 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
Cách sử dụng: Bây giờ bạn sẽ lặp lại tất cả các khung nhìn phụ và thao tác chúng khi cần.
//disable all text fields
for(UIView *v in [self.view allSubViews])
{
if([v isKindOfClass:[UITextField class]])
{
((UITextField*)v).enabled=NO;
}
}
allSubViews
chức năng: bạn phải tạo mảng dưới dạng [[[NSMutableArray alloc] init] autorelease]
hoặc dưới dạng [NSMutableArray array]
(giống nhau).
Đây là một triển khai Swift khác:
extension UIView {
var allSubviews: [UIView] {
return self.subviews.flatMap { [$0] + $0.allSubviews }
}
}
Một giải pháp trong Swift 3 cung cấp tất cả subviews
mà không bao gồm chế độ xem:
extension UIView {
var allSubViews : [UIView] {
var array = [self.subviews].flatMap {$0}
array.forEach { array.append(contentsOf: $0.allSubViews) }
return array
}
}
nil
các phần tử khỏi một mảng, để đảm bảo an toàn khi gọi các lượt xem phụ allSubViews
.
Tôi gắn thẻ mọi thứ khi nó được tạo. Sau đó, thật dễ dàng để tìm thấy bất kỳ lượt xem phụ nào.
view = [aView viewWithTag:tag];
Vừa tìm thấy một cách thú vị để thực hiện việc này thông qua trình gỡ lỗi:
http://idevrecipes.com/2011/02/10/exploring-iphone-view-hierarchies/
tham chiếu Apple Technote này:
https://developer.apple.com/library/content/technotes/tn2239/_index.html#SECUIKIT
Chỉ cần đảm bảo rằng trình gỡ lỗi của bạn bị tạm dừng (đặt điểm ngắt để tạm dừng nó theo cách thủ công) và bạn có thể yêu cầu recursiveDescription
.
Đây là một ví dụ với chức năng lặp và ngắt chế độ xem thực tế.
Nhanh:
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) }
}
}
}
Ví dụ về cuộc gọi:
mainView.loopViewHierarchy { (view, stop) in
if view is UIButton {
/// use the view
stop = true
}
}
Vòng lặp đảo ngược:
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
}
}
Ví dụ về cuộc gọi:
mainView.loopViewHierarchyReversed { (view, stop) in
if view is UIButton {
/// use the view
stop = true
}
}
Mục tiêu-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
Ví dụ về cuộc gọi:
[mainView loopViewHierarchy:^(UIView* view, BOOL* stop) {
if ([view isKindOfClass:[UIButton class]]) {
/// use the view
*stop = YES;
}
}];
Với sự giúp đỡ của Ole Begemann. Tôi đã thêm một vài dòng để kết hợp khái niệm khối vào nó.
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
Sử dụng danh mục HierarchyLogging trong ViewController của bạn. Bây giờ bạn có quyền tự do với những gì bạn cần làm.
void (^ViewActionBlock)(UIView *) = ^(UIView *view) {
if ([view isKindOfClass:[UIButton class]]) {
NSLog(@"%@", view);
}
};
[self.view logViewHierarchy: ViewActionBlock];
Không cần tạo bất kỳ chức năng mới nào. Chỉ làm điều đó khi gỡ lỗi bằng Xcode.
Đặt một điểm ngắt trong bộ điều khiển chế độ xem và đặt ứng dụng tạm dừng tại điểm ngắt này.
Nhấp chuột phải vào vùng trống và nhấn "Thêm Biểu thức ..." trong cửa sổ Xem của Xcode.
Nhập dòng này:
(NSString*)[self->_view recursiveDescription]
Nếu giá trị quá dài, hãy nhấp chuột phải vào nó và chọn "In Mô tả của ...". Bạn sẽ thấy tất cả các lượt xem phụ của self.view trong cửa sổ bảng điều khiển. Thay đổi chế độ xem self -> _ sang thứ khác nếu bạn không muốn xem các lượt xem phụ của self.view.
Làm xong! Không có gdb!
Đây là một đoạn mã đệ quy: -
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];
}
}
Nhân tiện, tôi đã thực hiện một dự án mã nguồn mở để trợ giúp loại nhiệm vụ này. Nó thực sự dễ dàng và sử dụng các khối Objective-C 2.0 để thực thi mã trên tất cả các chế độ xem trong một hệ thống phân cấp.
https://github.com/egold/UIViewRecursion
Thí dụ:
-(void)makeAllSubviewsGreen
{
[self.view runBlockOnAllSubviews:^(UIView *view) {
view.backgroundColor = [UIColor greenColor];
}];
}
Đây là một biến thể cho câu trả lời của Ole Begemann ở trên, nó bổ sung thêm thụt lề để minh họa hệ thống phân cấp:
// 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
Mã được đăng trong câu trả lời này đi qua tất cả các cửa sổ và tất cả các chế độ xem và tất cả các chế độ xem phụ của chúng. Nó được sử dụng để kết xuất bản in của hệ thống phân cấp chế độ xem sang NSLog nhưng bạn có thể sử dụng nó làm cơ sở cho bất kỳ quá trình truyền tải nào của hệ thống phân cấp chế độ xem. Nó sử dụng một hàm C đệ quy để duyệt qua cây xem.
Tôi đã viết một chuyên mục một thời gian trở lại để gỡ lỗi một số quan điểm.
IIRC, mã đã đăng là mã đã hoạt động. Nếu không, nó sẽ chỉ bạn đi đúng hướng. Sử dụng có rủi ro riêng, v.v.
Tôi nghĩ rằng tất cả các câu trả lời bằng cách sử dụng đệ quy (ngoại trừ tùy chọn trình gỡ lỗi) đã sử dụng các danh mục. Nếu bạn không cần / muốn một danh mục, bạn chỉ có thể sử dụng một phương thức thể hiện. Ví dụ: nếu bạn cần lấy một mảng tất cả các nhãn trong hệ thống phân cấp chế độ xem của mình, bạn có thể làm điều này.
@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
}
}
Phương thức bên dưới tạo một hoặc nhiều mảng có thể thay đổi, sau đó lặp qua các lần xem con của dạng xem đầu vào. Khi làm như vậy, nó sẽ thêm lượt xem phụ ban đầu, sau đó truy vấn xem có bất kỳ lượt xem phụ nào của lượt xem phụ đó hay không. Nếu đúng, nó sẽ tự gọi lại. Nó làm như vậy cho đến khi tất cả các chế độ xem của hệ thống phân cấp đã được thêm vào.
-(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;
}
Gọi bằng:
NSArray * subviews = [self allSubs:someView];