TTTAttributedLabel clickable link does not work in Swift 3 - uilabel

I just upgraded to Swift 3. I created a TTTAttributedLabel with a clickable URL, unfortunately the delegate function does not work anymore. Anyone has similar problems?
func attributedLabel(_ label: TTTAttributedLabel!, didSelectLinkWith url: URL!) {
}

Did you remember to connect the delegate?
label.delegate = self
It is working for me

extension YourViewController: TTTAttributedLabelDelegate {
func attributedLabel(_ label: TTTAttributedLabel!, didSelectLinkWith url: URL!) {
UIApplication.shared.openURL(url)
}
}

Related

PDF view - view not showing unless zoom is out

I'm working on a swiftUI app where I have to display multiple PDF files in one screen.
I've created a PDFView:
struct PDFKitRepresentedView: UIViewRepresentable {
let url: URL
init(_ url: URL) {
self.url = url
}
func makeUIView(context: UIViewRepresentableContext<PDFKitRepresentedView>) -> PDFKitRepresentedView.UIViewType {
let pdfView = PDFView()
let pdfDocument = PDFDocument(url: self.url)
pdfView.document = pdfDocument
return pdfView
}
func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<PDFKitRepresentedView>) {
// Update the view.
}
}
struct PDFKitView: View {
var url: URL
var body: some View {
PDFKitRepresentedView(url)
}
}
The PDF is created here:
if let url = attachment.path {
PDFKitView(url: url)
.frame(width: UIScreen.screenWidth - 40, height: UIScreen.screenHeight - 40, alignment: .center)
.padding()
}
The problem I'm having is that, whenever the first document is multipage, in order to see the other documents the user has first to pinch to zoom out completely and the other PDF's are shown in sequence.
I've tried to add this values, but that just makes the content of the PDF to disappear
I was wondering if setting the frame on the PDFKitView directly could be causing the issue, but no.
Anyone has any suggestions on how to make this work? I assume that if I could make the pdf to show already with a min zoom it would display the view correctly.
I finally found the answer on this post How to detect where NaN is passing to CoreGraphics API on Mac OS X 10.9

SwiftUI: Is it possible to turn off predictive text for a TextField

I would like to turn off predictive text/autocorrect for a TextField in SwiftUI. Looks like this was possible in with UITextField:
Disable UITextField Predictive Text
I checked the Apple documentation for TextField and googled, but can't find anything about this.
Has anyone found a way to disable the predictive text/autocomplete for a TextField?
Thank you!
Seems like it is now possible using Xcode 11 Beta 5. There is a new modifier to disable the autocorrection on TextField
func disableAutocorrection(_ disable: Bool?) -> some View
https://developer.apple.com/documentation/swiftui/textfield/3367734-disableautocorrection?changes=latest_beta
Edit:
Modifier disableAutocorrection is deprecated in iOS 16.1. The new modifier is autocorrectionDisabled:
func autocorrectionDisabled(_ disable: Bool = true) -> some View
https://developer.apple.com/documentation/swiftui/presentedwindowcontent/autocorrectiondisabled(_:)?changes=latest_beta&language=_5
This should work:
.disableAutocorrection(true)
Turns out you need to set the keyboard type to .alphabet for .disableAutocorrection(true) to work.
here is the extension I use:
extension View {
func removePredictiveSuggestions() -> some View {
self.keyboardType(.alphabet)
.disableAutocorrection(true)
}
}
Xcode 12.3 Swift 5.3
If you need to disable autocorrection on multiple TextFields, or indeed add other modifiers, then create a custom TextField:
struct TextFieldCustom: View {
let title: String
let text: Binding<String>
init(_ title: String, text: Binding<String>) {
self.title = title
self.text = text
}
var body: some View {
TextField(title, text: text)
.disableAutocorrection(true)
// add any other modifiers that you want
}
}
Example Usage:
Form {
Section(header: Text("Details")) {
TextFieldCustom("Field1", text: $field1)
TextFieldCustom("Feild2", text: $field2)
TextFieldCustom("Field3", text: $field3)
}
}
For the iOS 16.2 SDK, I needed to do this:
.keyboardType(.alphabet)
.textContentType(.oneTimeCode)
.autocorrectionDisabled(true)
(oneTimePasscode is an old UIKit hack to achieve the same result. It feels dirty to me, and I wouldn't be surprised if this behaviour changes again in the future...)

How to change PreferredStatusBarStyle programmatically

I'd like to change the color of status bar from white to black by press button, programmatically only in a single-ViewController
This is the code:
- (UIStatusBarStyle)preferredStatusBarStyle {
NSLog(#"PreferredStatusBarStyle");
if(nav_bar.alpha==1)
{
NSLog(#"->UIStatusBarStyleBlackOpaque");
return UIStatusBarStyleBlackOpaque;
}
else
{
NSLog(#"->UIStatusBarStyleLightContent");
return UIStatusBarStyleLightContent;
}}
then When I press a button action is:
[self setNeedsStatusBarAppearanceUpdate];
But this doesn't work!
When I press the button log write the correct status according to navbar.alpha, but statusbar text color remain UIStatusBarStyleBlackOpaque like when view appear.
setStatusBarStyle:animated: has been deprecated. In iOS9 you can achieve the same thing using preferredStatusBarStyle and setNeedsStatusBarAppearanceUpdate.
In your View Controller:
override func preferredStatusBarStyle() -> UIStatusBarStyle {
if (condition) {
return .LightContent
}
return .Default
}
And then when your condition changes:
self.setNeedsStatusBarAppearanceUpdate()
On Swift 4:
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
Hope it helps anybody else to find this post.
what you need to do, is to call the -setStatusBarStyle:animated: method thru the shared application, like this
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent animated:YES];
you can use it without the animated parameter as well. keep in mind that UIStatusBarStyleBlackOpaque is deprecated in iOS 7, documentation says you wanna use UIStatusBarStyleLightContent instead
edit:
sorry, if u wanna use preferredStatusBarStyle, take a look at this preferredStatusBarStyle isn't called

UICollectionView exception in UICollectionViewLayoutAttributes from iOS7

I have done a View in CollectionView with CustomLayout. In iOS6 it worked great but iOS7 it throws an exception like this.
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason:
'layout attributes for supplementary item at index path ( {length = 2, path = 0 - 0}) changed from CustomSupplementaryAttributes: 0xd1123a0 index path: (NSIndexPath: 0xd112580 {length = 2, path = 0 - 0}); element kind: (identifier); frame = (0 0; 1135.66 45); zIndex = -1; to CustomSupplementaryAttributes: 0xd583c80 index path: (NSIndexPath: 0xd583c70 {length = 2, path = 0 - 0}); element kind: (identifier); frame = (0 0; 1135.66 45); zIndex = -1; without invalidating the layout'
iOS 10
At iOS 10, a new feature is introduced, it is Cell Prefetching. It will let dynamic position of SupplementaryView crash. In order to run in the old behavior, it needs to disable prefetchingEnabled. It's true by default at iOS 10.
// Obj-C
// This function is available in iOS 10. Disable it for dynamic position of `SupplementaryView `.
if ([self.collectionView respondsToSelector:#selector(setPrefetchingEnabled:)]) {
self.collectionView.prefetchingEnabled = false;
}
// Swift
if #available(iOS 10, *) {
// Thanks #maksa
collectionView.prefetchingEnabled = false
// Swift 3 style
colView.isPrefetchingEnabled = false
}
I hate this problem. I spend 2 days for this problem.
A reference about Cell Pre-fetch #iOS 10.
iOS 9 and before ...
#Away Lin is right.. I solve the same problem by implementing that delegate method.
My Custom UICollectionViewLayout will modify the attributes in layoutAttributesForElementsInRect. The section position is dynamic, not static. So, I obtain warnings about the layout attributes for supplementary item at index path ... changed from ... to .... Before the changes, invalideLayout related methods should be called.
And, after implementing this delegate method to return true, the method invalidateLayoutWithContext: will be called when scrolling the UICollectionViewLayout. By default, it returns false.
- (BOOL) shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
return YES;
}
From Apple Docs
Return Value
true if the collection view requires a layout update or false if the
layout does not need to change.
Discussion The default implementation of this method returns false.
Subclasses can override it and return an appropriate value based on
whether changes in the bounds of the collection view require changes
to the layout of cells and supplementary views.
If the bounds of the collection view change and this method returns
true, the collection view invalidates the layout by calling the
invalidateLayoutWithContext: method.
Availability Available in iOS 6.0 and later.
And more ...
A nice example project on GitHub, for custom UICollectionViewLayout.
You need to invalidate the existing layout before updating, see the end of the error message:
without invalidating the layout'
[collectionViewLayout invalidateLayout];
Apple Documentation for UICollectionViewLayout
I had the same exception: in iOS 7, you need now to override the inherited isEqual: in your UICollectionViewLayoutAttributes subclass as stated in Apple documentation here.
I solved my problem by override the method at the subclase of UICollectionViewFlowLayout:
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBound
return YES
I'm not entirely certain how or why, but this appears to be fixed in iOS 12, supporting both supplementary view resizing and prefetching. The trick for me was to make sure things are happening in the correct order.
Here is a working implementation of a stretchable header view. Notice the implementation of the header resizing happening in layoutAttributesForElements(in rect: CGRect):
class StretchyHeaderLayout: UICollectionViewFlowLayout {
var cache = [UICollectionViewLayoutAttributes]()
override func prepare() {
super.prepare()
cache.removeAll()
guard let collectionView = collectionView else { return }
let sections = [Int](0..<collectionView.numberOfSections)
for section in sections {
let items = [Int](0..<collectionView.numberOfItems(inSection: section))
for item in items {
let indexPath = IndexPath(item: item, section: section)
if let attribute = layoutAttributesForItem(at: indexPath) {
cache.append(attribute)
}
}
}
if let header = layoutAttributesForSupplementaryView(ofKind: StretchyCollectionHeaderKind, at: IndexPath(item: 0, section: 0)) {
cache.append(header)
}
}
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
return true
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let visibleAttributes = cache.filter { rect.contains($0.frame) || rect.intersects($0.frame) }
guard let collectionView = collectionView else { return visibleAttributes }
// Find the header and stretch it while scrolling.
guard let header = visibleAttributes.filter({ $0.representedElementKind == StretchyCollectionHeaderKind }).first else { return visibleAttributes }
header.frame.origin.y = collectionView.contentOffset.y
header.frame.size.height = headerHeight.home - collectionView.contentOffset.y
header.frame.size.width = collectionView.frame.size.width
return visibleAttributes
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
let attributes = super.layoutAttributesForItem(at: indexPath as IndexPath)?.copy() as! UICollectionViewLayoutAttributes
guard collectionView != nil else { return attributes }
attributes.frame.origin.y = headerHeight.home + attributes.frame.origin.y
return attributes
}
override func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
return UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: StretchyCollectionHeaderKind, with: indexPath)
}
override var collectionViewContentSize: CGSize {
get {
guard let collectionView = collectionView else { return .zero }
let numberOfSections = collectionView.numberOfSections
let lastSection = numberOfSections - 1
let numberOfItems = collectionView.numberOfItems(inSection: lastSection)
let lastItem = numberOfItems - 1
guard let lastCell = layoutAttributesForItem(at: IndexPath(item: lastItem, section: lastSection)) else { return .zero }
return CGSize(width: collectionView.frame.width, height: lastCell.frame.maxY + sectionInset.bottom)
}
}
}
P.S.: I'm aware the cache doesn't actually serve any purpose at this point :)
I had this problem too, because I had code that depended on the content size of the collection view. My code was accessing the content size via the collectionView!.contentSize instead of collectionViewContentSize.
The former uses the collectionView property of UICollectionViewLayout, while the latter uses the custom-implemented layout property. In my code, the first time the layout was asked for attributes, contentSize had not been set yet.
Select the CollectionView and Goto Attribute Inspector. Uncheck The Prefetching Enabled CheckBox. This is Fixed my Issue. Screenshot

Implementing "Show in Finder" button in Objective-C

In my application I would like to create a 'Show in Finder' button.
I have been able to figure out how to pop up a Finder window of that directory but haven't figured out how to highlight the file like the OS does.
Is this possible?
NSArray *fileURLs = [NSArray arrayWithObjects:fileURL1, /* ... */ nil];
[[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs:fileURLs];
stolen from
Launch OSX Finder window with specific files selected
You can use NSWorkspace method -selectFile:inFileViewerRootedAtPath: like this:
[[NSWorkspace sharedWorkspace] selectFile:fullPathString inFileViewerRootedAtPath:pathString];
Its worth mentioning that owen's method only works from osx 10.6 or later (Ref: https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/NSWorkspace_Class/Reference/Reference.html ).
So if your writing something to run on the older generations its probably better to do it in the way suggested by justin as its not been deprecated (yet).
// Place the following code within your Document subclass
// enable or disable the menu item called "Show in Finder"
override func validateUserInterfaceItem(anItem: NSValidatedUserInterfaceItem) -> Bool {
if anItem.action() == #selector(showInFinder) {
return self.fileURL?.path != nil;
} else {
return super.validateUserInterfaceItem(anItem)
}
}
// action for the "Show in Finder" menu item, etc.
#IBAction func showInFinder(sender: AnyObject) {
func showError() {
let alert = NSAlert()
alert.messageText = "Error"
alert.informativeText = "Sorry, the document couldn't be shown in the Finder."
alert.runModal()
}
// if the path isn't known, then show an error
let path = self.fileURL?.path
guard path != nil else {
showError()
return
}
// try to select the file in the Finder
let workspace = NSWorkspace.sharedWorkspace()
let selected = workspace.selectFile(path!, inFileViewerRootedAtPath: "")
if !selected {
showError()
}
}