Skip to content

Commit 0ea5137

Browse files
Fixes bug where the most relevant size wasn't used (#38)
* Fixes bug where the most relevant size wasn't used The comment stated that window?.windowScene is nil if superview is set, but in practice it's been observed that this is not the case, meaning that the screen's dimensions was always preferred over the size of the superview. * Adds a link tap test that would have failed before * Lintfix
1 parent 9f8f51c commit 0ea5137

File tree

2 files changed

+160
-32
lines changed

2 files changed

+160
-32
lines changed

Sources/LCLabel/LCLabel.swift

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -226,11 +226,12 @@ final public class LCLabel: UILabel {
226226
/// Returns intrinsicContentSize of the current label
227227
public override var intrinsicContentSize: CGSize {
228228
let size: CGSize
229-
// Getting the width of the current window, or the width
230-
// of the super view since `window?.windowScene` will be nil
231-
// if superview is set.
232-
let width = window?.windowScene?.screen.bounds.width ??
233-
superview?.bounds.width
229+
230+
// Use the full available width from our superview,
231+
// or from the current window if no superview is available
232+
let width = superview?.bounds.width ??
233+
window?.windowScene?.screen.bounds.width
234+
234235
if let width = width {
235236
size = CGSize(
236237
width: width,

Tests/LCLabelTests/LCLabelTests.swift

Lines changed: 154 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ final class LCLabelTests: XCTestCase {
2121
])
2222
let label = createLabel(
2323
text: attStr,
24-
frame: CGRect(x: 0, y: 0, width: 300, height: 40))
25-
label.numberOfLines = 1
24+
frame: CGRect(x: 0, y: 0, width: 300, height: 40),
25+
numberOfLines: 1)
2626
let failure = verifySnapshot(
2727
matching: label,
2828
as: .image,
@@ -45,8 +45,8 @@ final class LCLabelTests: XCTestCase {
4545
])
4646
let label = createLabel(
4747
text: attStr,
48-
frame: CGRect(x: 0, y: 0, width: 300, height: 40))
49-
label.numberOfLines = 1
48+
frame: CGRect(x: 0, y: 0, width: 300, height: 40),
49+
numberOfLines: 1)
5050
let failure = verifySnapshot(
5151
matching: label,
5252
as: .image,
@@ -64,8 +64,8 @@ final class LCLabelTests: XCTestCase {
6464
])
6565
let label = createLabel(
6666
text: attStr,
67-
frame: CGRect(x: 0, y: 0, width: 300, height: 40))
68-
label.numberOfLines = 1
67+
frame: CGRect(x: 0, y: 0, width: 300, height: 40),
68+
numberOfLines: 1)
6969
label.centeringTextAlignment = .top
7070
let failure = verifySnapshot(
7171
matching: label,
@@ -84,8 +84,8 @@ final class LCLabelTests: XCTestCase {
8484
])
8585
let label = createLabel(
8686
text: attStr,
87-
frame: CGRect(x: 0, y: 0, width: 300, height: 40))
88-
label.numberOfLines = 1
87+
frame: CGRect(x: 0, y: 0, width: 300, height: 40),
88+
numberOfLines: 1)
8989
label.centeringTextAlignment = .bottom
9090
let failure = verifySnapshot(
9191
matching: label,
@@ -104,8 +104,8 @@ final class LCLabelTests: XCTestCase {
104104
])
105105
let label = createLabel(
106106
text: attStr,
107-
frame: CGRect(x: 0, y: 0, width: 300, height: 40))
108-
label.numberOfLines = 1
107+
frame: CGRect(x: 0, y: 0, width: 300, height: 40),
108+
numberOfLines: 1)
109109
label.lineBreakMode = .byTruncatingTail
110110
let failure = verifySnapshot(
111111
matching: label,
@@ -124,8 +124,8 @@ final class LCLabelTests: XCTestCase {
124124
])
125125
let label = createLabel(
126126
text: attStr,
127-
frame: CGRect(x: 0, y: 0, width: 300, height: 40))
128-
label.numberOfLines = 1
127+
frame: CGRect(x: 0, y: 0, width: 300, height: 40),
128+
numberOfLines: 1)
129129
label.lineBreakMode = .byTruncatingMiddle
130130
let failure = verifySnapshot(
131131
matching: label,
@@ -145,8 +145,8 @@ final class LCLabelTests: XCTestCase {
145145
])
146146
let label = createLabel(
147147
text: attStr,
148-
frame: CGRect(x: 0, y: 0, width: 300, height: 40))
149-
label.numberOfLines = 1
148+
frame: CGRect(x: 0, y: 0, width: 300, height: 40),
149+
numberOfLines: 1)
150150
label.textInsets = UIEdgeInsets(top: 0, left: 30, bottom: 0, right: 30)
151151
let failure = verifySnapshot(
152152
matching: label,
@@ -171,8 +171,8 @@ final class LCLabelTests: XCTestCase {
171171
]))
172172
let label = createLabel(
173173
text: attStr,
174-
frame: CGRect(x: 0, y: 0, width: 300, height: 40))
175-
label.numberOfLines = 2
174+
frame: CGRect(x: 0, y: 0, width: 300, height: 40),
175+
numberOfLines: 2)
176176
let failure = verifySnapshot(
177177
matching: label,
178178
as: .image,
@@ -196,9 +196,9 @@ final class LCLabelTests: XCTestCase {
196196
]))
197197
let label = createLabel(
198198
text: attStr,
199-
frame: CGRect(x: 0, y: 0, width: 300, height: 40))
199+
frame: CGRect(x: 0, y: 0, width: 300, height: 40),
200+
numberOfLines: 0)
200201
label.lineFragmentPadding = 10
201-
label.numberOfLines = 0
202202
let failure = verifySnapshot(
203203
matching: label,
204204
as: .image,
@@ -222,8 +222,8 @@ final class LCLabelTests: XCTestCase {
222222
]))
223223
let label = createLabel(
224224
text: attStr,
225-
frame: CGRect(x: 0, y: 0, width: 300, height: 40))
226-
label.numberOfLines = 2
225+
frame: CGRect(x: 0, y: 0, width: 300, height: 40),
226+
numberOfLines: 2)
227227
let failure = verifySnapshot(
228228
matching: label,
229229
as: .image,
@@ -248,8 +248,8 @@ final class LCLabelTests: XCTestCase {
248248
]))
249249
let label = createLabel(
250250
text: attStr,
251-
frame: CGRect(x: 0, y: 0, width: 300, height: 40))
252-
label.numberOfLines = 2
251+
frame: CGRect(x: 0, y: 0, width: 300, height: 40),
252+
numberOfLines: 2)
253253
label.linkStyleValidation = .ensure
254254
let text = label.attributedText
255255
let range = NSRange(location: 0, length: attStr.length)
@@ -477,8 +477,8 @@ final class LCLabelTests: XCTestCase {
477477
context: nil)
478478
let label = createLabel(
479479
text: attStr,
480-
frame: CGRect(x: 0, y: 0, width: width, height: size.height))
481-
label.numberOfLines = 0
480+
frame: CGRect(x: 0, y: 0, width: width, height: size.height),
481+
numberOfLines: 0)
482482
let failure = verifySnapshot(
483483
matching: label,
484484
as: .image,
@@ -604,8 +604,8 @@ final class LCLabelTests: XCTestCase {
604604

605605
let label = createLabel(
606606
text: attStr,
607-
frame: .zero)
608-
label.numberOfLines = 0
607+
frame: .zero,
608+
numberOfLines: 0)
609609
label.translatesAutoresizingMaskIntoConstraints = false
610610
let superview = UIView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
611611
superview.backgroundColor = .purple
@@ -818,8 +818,93 @@ final class LCLabelTests: XCTestCase {
818818
XCTAssertEqual(label.hitTest(CGPoint(x: 20, y: 25), with: nil), label)
819819
}
820820

821+
func testMultilineLinkTaps() {
822+
let termsOfServiceString = "Terms of service"
823+
let privacyPolicyString = "Privacy policy"
824+
let termsOfServiceURL = URL(string: "lclabel://tos")!
825+
let privacyPolicyURL = URL(string: "lclabel://p")!
826+
let string =
827+
"You must accept \(termsOfServiceString) and \(privacyPolicyString)"
828+
let attributedString = NSMutableAttributedString(string: string)
829+
let termsOfServiceRange = attributedString.mutableString
830+
.range(of: termsOfServiceString)
831+
let privacyPolicyRange = attributedString.mutableString
832+
.range(of: privacyPolicyString)
833+
let firstRowTermsOfServiceTouchPoint = CGPoint(x: 115, y: 15)
834+
let secondRowTermsOfServiceTouchPoint = CGPoint(x: 20, y: 26)
835+
let privacyPolicyTouchPoint = CGPoint(x: 102, y: 26)
836+
let label = LCLabel()
837+
let containerView = UIView()
838+
let viewController = UIViewController()
839+
let window = makeVisibleInWindow(viewController)
840+
841+
attributedString.addAttributes(
842+
[.lclabelLink: termsOfServiceURL],
843+
range: termsOfServiceRange)
844+
attributedString.addAttributes(
845+
[.lclabelLink: privacyPolicyURL],
846+
range: privacyPolicyRange)
847+
848+
label.translatesAutoresizingMaskIntoConstraints = false
849+
label.isUserInteractionEnabled = true
850+
label.textInsets = .zero
851+
label.lineFragmentPadding = 0
852+
label.numberOfLines = 0
853+
label.delegate = self
854+
label.setContentCompressionResistancePriority(.required, for: .vertical)
855+
label.attributedText = attributedString
856+
label.font = .systemFont(ofSize: 11)
857+
858+
containerView.translatesAutoresizingMaskIntoConstraints = false
859+
860+
viewController.view.addSubview(containerView)
861+
containerView.addSubview(label)
862+
863+
NSLayoutConstraint.activate([
864+
containerView.widthAnchor.constraint(equalToConstant: 140),
865+
containerView.topAnchor
866+
.constraint(equalTo: viewController.view.topAnchor),
867+
containerView.leadingAnchor
868+
.constraint(equalTo: viewController.view.leadingAnchor),
869+
870+
label.heightAnchor.constraint(equalToConstant: 40.0),
871+
label.topAnchor.constraint(equalTo: containerView.topAnchor),
872+
label.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
873+
label.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
874+
label.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
875+
])
876+
877+
let dispatchFinishedExpectation =
878+
expectation(description: "waitForDispatch")
879+
880+
XCTAssertNil(tappedURL)
881+
882+
// Perform a async dispatch on main to allow a draw cycle to complete before performing tests on the label
883+
DispatchQueue.main.async {
884+
self.touch(label, at: firstRowTermsOfServiceTouchPoint)
885+
XCTAssertEqual(self.tappedURL, termsOfServiceURL)
886+
887+
self.touch(label, at: privacyPolicyTouchPoint)
888+
XCTAssertEqual(self.tappedURL, privacyPolicyURL)
889+
890+
self.touch(label, at: secondRowTermsOfServiceTouchPoint)
891+
XCTAssertEqual(self.tappedURL, termsOfServiceURL)
892+
893+
self.touch(label, at: CGPoint(x: -1000, y: -1000))
894+
XCTAssertEqual(self.tappedURL, termsOfServiceURL)
895+
896+
window.isHidden = true
897+
898+
dispatchFinishedExpectation.fulfill()
899+
}
900+
901+
wait(for: [dispatchFinishedExpectation], timeout: 1.0)
902+
}
903+
821904
// MARK: Private
822905

906+
private var tappedURL: URL?
907+
823908
private func createLabel(
824909
text: NSMutableAttributedString,
825910
frame: CGRect,
@@ -830,11 +915,25 @@ final class LCLabelTests: XCTestCase {
830915
label.frame = frame
831916
label.centeringTextAlignment = alignment
832917
label.isUserInteractionEnabled = true
833-
label.numberOfLines = 1
918+
label.numberOfLines = numberOfLines
834919
label.attributedText = text
835920
label.backgroundColor = .black
836921
return label
837922
}
923+
924+
private func touch(_ label: LCLabel, at point: CGPoint) {
925+
let mockTouch = MockTouch(location: point)
926+
let touches: Set<UITouch> = [mockTouch]
927+
928+
label.touchesBegan(touches, with: nil)
929+
label.touchesEnded(touches, with: nil)
930+
}
931+
}
932+
933+
extension LCLabelTests: LCLabelDelegate {
934+
func didPress(url: URL, at point: CGPoint) {
935+
tappedURL = url
936+
}
838937
}
839938

840939
extension XCTestCase {
@@ -845,5 +944,33 @@ extension XCTestCase {
845944
.deletingLastPathComponent()
846945
return "\(fileUrl.path)/Sources/LCLabel/LCLabel.docc/Resources/__snapshots__"
847946
}
947+
}
848948

949+
extension XCTestCase {
950+
func makeVisibleInWindow(_ viewController: UIViewController) -> UIWindow {
951+
let window = UIWindow()
952+
953+
window.rootViewController = viewController
954+
window.makeKeyAndVisible()
955+
956+
return window
957+
}
958+
}
959+
960+
private class MockTouch: UITouch {
961+
962+
// MARK: Lifecycle
963+
964+
required init(location: CGPoint) {
965+
self.location = location
966+
super.init()
967+
}
968+
969+
// MARK: Internal
970+
971+
let location: CGPoint
972+
973+
override func location(in view: UIView?) -> CGPoint {
974+
location
975+
}
849976
}

0 commit comments

Comments
 (0)