-
Notifications
You must be signed in to change notification settings - Fork 35
Make view alignment not sensitive to current frame #146
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -115,6 +115,80 @@ extension Alignable { | |
return sourcePoint.offset(to: targetPoint) | ||
} | ||
|
||
/// Calculates the offset between two views' positions, ignoring any transforms in the view hierarchy. | ||
/// | ||
/// - precondition: The receiver and `otherView` must be in the same view hierarchy. | ||
/// | ||
/// - parameter position: The position in the receiving view's untransformed frame. | ||
/// - parameter otherView: The other view for the measurement. | ||
/// - parameter otherPosition: The position in the `otherView`'s untransformed frame to use for the measurement. | ||
/// - returns: The offset from the receiver's `position` to the `otherView`'s `otherPosition`. | ||
public func untransformedFrameOrigin( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A couple things to consider:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah I'm struggling with this function name + parameter names |
||
toAlign position: Position, | ||
to otherView: Alignable, | ||
_ otherPosition: Position, | ||
alignmentBehavior: TargetAlignmentBehavior = .automatic | ||
) throws -> CGPoint { | ||
let receiverContext = alignmentContext | ||
let receiverView = receiverContext.view | ||
|
||
let targetContext = otherView.alignmentContext | ||
let targetView = targetContext.view | ||
|
||
// We can't be aligned to another view if we don't have a superview. | ||
guard let superview = receiverView.superview else { | ||
ParalayoutAlertForInvalidViewHierarchy() | ||
return .zero | ||
} | ||
|
||
switch position { | ||
case .topLeft, .topRight, .leftCenter, .rightCenter, .bottomLeft, .bottomRight: | ||
switch otherPosition { | ||
case .topLeading, .topTrailing, .leadingCenter, .trailingCenter, .bottomLeading, .bottomTrailing: | ||
ParalayoutAlertForMismatchedAlignmentPositionTypes() | ||
default: | ||
break | ||
} | ||
|
||
case .topLeading, .topTrailing, .leadingCenter, .trailingCenter, .bottomLeading, .bottomTrailing: | ||
switch otherPosition { | ||
case .topLeft, .topRight, .leftCenter, .rightCenter, .bottomLeft, .bottomRight: | ||
ParalayoutAlertForMismatchedAlignmentPositionTypes() | ||
default: | ||
break | ||
} | ||
|
||
default: | ||
break | ||
} | ||
|
||
let targetIsInSourceSuperviewChain = sequence(first: receiverView, next: { $0.superview }).contains(targetView) | ||
|
||
let targetPoint: CGPoint | ||
switch alignmentBehavior { | ||
case .bounds, | ||
.automatic where targetIsInSourceSuperviewChain: | ||
targetPoint = try superview.untransformedConvert( | ||
targetContext | ||
.pointInBounds(at: otherPosition) | ||
.offset( | ||
by: UIOffset(horizontal: -targetView.bounds.origin.x, vertical: -targetView.bounds.origin.y) | ||
), | ||
from: targetView | ||
) | ||
|
||
case .untransformedFrame, | ||
.automatic /* where !targetIsInSourceSuperviewChain */: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. boy oh boy do I wish I knew why this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's referencing the opposite There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, exactly. Or at least the compiler wasn't clever enough to figure this out when I wrote it like 4 compiler versions ago 😂 It may have gotten smarter about it since then. |
||
targetPoint = try superview.untransformedConvert( | ||
targetContext.pointInBounds(at: otherPosition), | ||
from: targetView | ||
) | ||
} | ||
|
||
let sourcePoint = receiverContext.pointInBounds(at: position) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this line is quite right. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (although you can't just add back the |
||
return targetPoint.offset(by: .init(horizontal: -sourcePoint.x, vertical: -sourcePoint.y)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So I think this is correct for the base case of calling There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You know what I know there. Need to defer to @NickEntin There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. these last two lines are the only difference between the above method and this one, right? |
||
} | ||
|
||
/// Move the view to align it with another view. | ||
/// | ||
/// - precondition: The receiver and the `otherView` must be in the same view hierarchy. | ||
|
@@ -135,17 +209,14 @@ extension Alignable { | |
) { | ||
do { | ||
let receiverView = alignmentContext.view | ||
receiverView.untransformedFrame.origin = receiverView.untransformedFrame.origin | ||
.offset( | ||
by: try untransformedFrameOffset( | ||
from: position, | ||
to: otherView, | ||
otherPosition, | ||
alignmentBehavior: alignmentBehavior | ||
) | ||
) | ||
.offset(by: offset) | ||
.roundedToPixel(in: receiverView) | ||
receiverView.untransformedFrame.origin = try untransformedFrameOrigin( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are two ways we could add test coverage here:
In either event, we should probably add the test in a distinct PR, merge it first, then rebase this PR against it with the fix. |
||
toAlign: position, | ||
to: otherView, | ||
otherPosition, | ||
alignmentBehavior: alignmentBehavior | ||
) | ||
.offset(by: offset) | ||
.roundedToPixel(in: receiverView) | ||
|
||
} catch { | ||
ParalayoutAlertForInvalidViewHierarchy() | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Heh, copy-pasta of the other method's comments. Needs updating