在两个UIView
个实例之间找到最低共同祖先的最有效方法是什么?
如果没有实施Lowest Common Ancestor,是否可以使用任何UIKit
API来查找它?
NSView
已ancestorSharedWithView:
,所以我怀疑这可能会在以后加入iOS。
我目前正在使用这种快速而肮脏的解决方案,如果给定的视图不是兄弟或直接祖先,则效率很低。
- (UIView*)lyt_ancestorSharedWithView:(UIView*)aView
{
if (aView == nil) return nil;
if (self == aView) return self;
if (self == aView.superview) return self;
UIView *ancestor = [self.superview lyt_ancestorSharedWithView:aView];
if (ancestor) return ancestor;
return [self lyt_ancestorSharedWithView:aView.superview];
}
(对于那些实施类似方法的人来说,Lyt项目的单元测试可能会有所帮助)
答案 0 :(得分:6)
使用-isDescendantOfView:。
并不太难- (UIView *)my_ancestorSharedWithView:(UIView *)aView
{
UIView *testView = self;
while (testView && ![aView isDescendantOfView:testView])
{
testView = [testView superview];
}
return testView;
}
答案 1 :(得分:4)
功能替代方案:
Swift(假设使用您最喜欢的OrderedSet
)
extension UIView {
func nearestCommonSuperviewWith(other: UIView) -> UIView {
return self.viewHierarchy().intersect(other.self.viewHierarchy()).first
}
private func viewHierarchy() -> OrderedSet<UIView> {
return Set(UIView.hierarchyFor(self, accumulator: []))
}
static private func hierarchyFor(view: UIView?, accumulator: [UIView]) -> [UIView] {
guard let view = view else {
return accumulator
}
return UIView.hierarchyFor(view.superview, accumulator: accumulator + [view])
}
}
Objective-C(在UIView
上作为类别实现,假设存在firstObjectCommonWithArray
方法)
+ (NSArray *)hierarchyForView:(UIView *)view accumulator:(NSArray *)accumulator
{
if (!view) {
return accumulator;
}
else {
return [self.class hierarchyForView:view.superview accumulator:[accumulator arrayByAddingObject:view]];
}
}
- (NSArray *)viewHierarchy
{
return [self.class hierarchyForView:self accumulator:@[]];
}
- (UIView *)nearestCommonSuperviewWithOtherView:(UIView *)otherView
{
return [[self viewHierarchy] firstObjectCommonWithArray:[otherView viewHierarchy]];
}
答案 2 :(得分:3)
这是一个更短的版本,作为UIView的一个类别:
- (UIView *)nr_commonSuperview:(UIView *)otherView
{
NSMutableSet *views = [NSMutableSet set];
UIView *view = self;
do {
if (view != nil) {
if ([views member:view])
return view;
[views addObject:view];
view = view.superview;
}
if (otherView != nil) {
if ([views member:otherView])
return otherView;
[views addObject:otherView];
otherView = otherView.superview;
}
} while (view || otherView);
return nil;
}
答案 3 :(得分:1)
您的实现仅在一次迭代中检查两个视图级别。
这是我的:
+ (UIView *)commonSuperviewWith:(UIView *)view1 anotherView:(UIView *)view2 {
NSParameterAssert(view1);
NSParameterAssert(view2);
if (view1 == view2) return view1.superview;
// They are in diffrent window, so they wont have a common ancestor.
if (view1.window != view2.window) return nil;
// As we don’t know which view has a heigher level in view hierarchy,
// We will add these view and their superview to an array.
NSMutableArray *mergedViewHierarchy = [@[ view1, view2 ] mutableCopy];
UIView *commonSuperview = nil;
// Loop until all superviews are included in this array or find a view’s superview in this array.
NSInteger checkIndex = 0;
UIView *checkingView = nil;
while (checkIndex < mergedViewHierarchy.count && !commonSuperview) {
checkingView = mergedViewHierarchy[checkIndex++];
UIView *superview = checkingView.superview;
if ([mergedViewHierarchy containsObject:superview]) {
commonSuperview = superview;
}
else if (checkingView.superview) {
[mergedViewHierarchy addObject:superview];
}
}
return commonSuperview;
}
答案 4 :(得分:1)
斯威夫特3:
extension UIView {
func findCommonSuperWith(_ view:UIView) -> UIView? {
var a:UIView? = self
var b:UIView? = view
var superSet = Set<UIView>()
while a != nil || b != nil {
if let aSuper = a {
if !superSet.contains(aSuper) { superSet.insert(aSuper) }
else { return aSuper }
}
if let bSuper = b {
if !superSet.contains(bSuper) { superSet.insert(bSuper) }
else { return bSuper }
}
a = a?.superview
b = b?.superview
}
return nil
}
}
答案 5 :(得分:0)
Swift 2.0:
rebuild
答案 6 :(得分:0)
我相信,使用以下方法可以最大限度地缩短时间复杂度和空间复杂度
Step1 :计算每个的深度。让我们考虑v1
和v2
是视图,d1
,d2
是相应的深度
第2步:如果d1 == d2
,请写一个for循环(index < d1 or d2
),选择v1.superView
和v2.superView
并进行比较。返回,如果他们是平等的。
Step3 :如果d1 > d2
,请取差(d1-d2)
,执行while循环,取v1.superView并减小d1值。 while循环应退出,如果(d1 == d2)
。之后,重复Step1。
Step4 :如果d2 > d1
,请取差(d2-d1)
,执行while循环,取v2.superView
并递减d2
值。 while循环应退出,如果(d2 == d1)
。之后,重复Step1。
答案 7 :(得分:0)
Carl Lindberg's solution的Swift 5版本:
func nearestCommonSuperviewWith(other: UIView) -> UIView? {
var nearestAncestor: UIView? = self
while let testView = nearestAncestor, !other.isDescendant(of: testView) {
nearestAncestor = testView.superview
}
return nearestAncestor
}
答案 8 :(得分:0)
我的数据库更长了一点,并且没有使用UIKit isDescendant函数。
方法1:使用在树中查找LCA的方法。时间复杂度:O(N),空间复杂度:(1)
func findCommonSuper(_ view1:inout UIView, _ view2:inout UIView) -> UIView? {
var level1 = findLevel(view1)
var level2 = findLevel(view2)
if level1 > level2 {
var dif = level1-level2
while dif > 0 {
view1 = view1.superview!
dif -= 1
}
} else if level1 < level2 {
var dif = level2-level1
while dif > 0 {
view2 = view2.superview!
dif -= 1
}
}
while view1 != view2 {
if view1.superview == nil || view2.superview == nil {
return nil
}
view1 = view1.superview!
view2 = view2.superview!
}
if view1 == view2 {
return view1
}
return nil
}
func findLevel(_ view:UIView) -> Int {
var level = 0
var view = view
while view.superview != nil {
view = view.superview!
level += 1
}
return level
}
方法2:插入一个视图的祖先进行设置,然后迭代第二个视图的祖先。时间复杂度:O(N),空间复杂度:O(N)
func findCommonSuper2(_ view1:UIView, _ view2:UIView) -> UIView? {
var set = Set<UIView>()
var view = view1
while true {
set.insert(view)
if view.superview != nil {
view = view.superview!
} else {
break
}
}
view = view2
while true {
if set.contains(view) {
return view
}
if view.superview != nil {
view = view.superview!
} else {
break
}
}
return nil
}