Why UICollectionView stops working when NSMutableAttributedString is set to one of its cells - uicollectionview

I stumbled upon a special problem in my current project.
We have UICollectionView using a custom layout called SquareMosaicLayout.
Within this collection view the first cell it presenting a UITextView.
This text view again shall show an html text created with:
NSMutableAttributedString(fromHTMLString: htmlString, textColor: textColor, font: font)
Now when this string is assigned to the text view the collection view somehow stops working which means it does not ask the datasource for new cells when scrolling through the collection view.
This results in the collection view showing blank space.
Unfortunately I did not have time to isolate the problem in terms of if the custom layout breaks anything but it definitely has to do with assigning the string. If we don't do that the collection view works as expected.

We still did not found the reason for the behaviour but a first workaround.
It is possible to set the NSMutableAttributedString asynchronously.
extension UITextView {
[ ... ]
DispatchQueue.main.async {
let optionalAttributedText = NSMutableAttributedString(fromHTMLString: htmlString, textColor: self.textColor, font: self.font)
guard let attributedText = optionalAttributedText else { return }
self.attributedText = attributedText
[ ... ]
In that case the UICollectionView behaves normal showing all the cells.

Related

NSTextBlock backgroundColor is not drawn

I have an NSTextBlock subclass that has a specific backgroundColor set. Now when I add a custom paragraph style to a range of text like this
let block = MyTextBlock()
block.backgroundColor = myBackgroundColor
let pstyle = NSMutableParagraphStyle()
pstyle.textBlocks = [block]
attributedText.addAttribute(.paragraphStyle, value: pstyle, range: textRange)
// Append the attributed string to the text views textStorage ...
the text block is shown without a background color. I know that the text blocks works, because rectForLayout gets called, but when I try to override drawBackground it never gets called.
Do I have to do something else for NSTextBlocks to draw their background?
PS: Borders also seem to be ignored. I also tried to find a sample on Github, but that also doesn't draw any backgrounds, despite having a background color set.
After trying everything, I finally managed to get the background to show up, by setting
setValue(100, type: .percentageValueType, for: .width)
It seems that the drawing logic expects some value for the content size. Really nice documentation job there. This requirement is nowhere to be found.

UITextField display abnormal

There is a inputBar in my viewController with a textField and two btns, it layout like this :
The orange part view is the textField, and the two btns I haven't set them backgroundColor.
When I try to continuously input some words while the content has already exceeded the scope of the textField displayed, the place where places the left switch btn, flashes a part of the textField content which is obscured.
This kind of situation will only appear in inputing Chinese character
There is my layout code for the view:
self.backgroundColor = .lightGray
self.addSubview(switchBtn)
switchBtn.titleLabel?.font = UIFont.systemFont(ofSize: 16)
switchBtn.setTitle("switch", for: .normal)
switchBtn.setTitleColor(.white, for: .normal)
switchBtn.snp.makeConstraints { (make) in
make.leading.equalToSuperview()
make.width.equalToSuperview().multipliedBy(1/8.0)
make.top.bottom.equalToSuperview()
}
self.addSubview(sendBtn)
sendBtn.titleLabel?.font = UIFont.systemFont(ofSize: 16)
sendBtn.setTitle("send", for: .normal)
sendBtn.setTitleColor(.white, for: .normal)
sendBtn.snp.makeConstraints { (make) in
make.width.equalTo(switchBtn)
make.top.bottom.trailing.equalToSuperview()
}
self.insertSubview(inputBar, at: 0)
inputBar.backgroundColor = .orange
inputBar.snp.makeConstraints { (make) in
make.leading.equalTo(switchBtn.snp.trailing)
make.top.equalToSuperview().offset(4)
make.bottom.equalToSuperview().offset(-4)
make.trailing.equalTo(sendBtn.snp.leading)
}
I have written the same function code use Objective-C, it occurs the same problem, Could you tell me where my problem is?
I don't have the rest of your code, so it's hard to say for sure, but a text field is designed to scroll. When clip subviews is off, the content that is scrolled outside your text field will still be visible.

NSPredicateEditor is clearing the value of its NSTextField

I'm using NSPredicateEditor in my project. One of my rows has 2 popup buttons and a text field on the right. If I have something typed in the text field, when I select a different menu item from either of the popup buttons then the text in the text field is deleted. That seems to be the default behavior and I don't want that text deleted. I've tried everything I can think of and can't seem to handle this. Any ideas how to change this behavior?
You could save the data by using NSUserDefault it will save and load the data (It won't delete your data unless the user does).
Access the text fields via the row template's templateViews property.
This allows you to get the custom values that have been entered into the textfield. Save the value when it changes.
class YourCustomRowTemplate : NSPredicateEditorRowTemplate {
func printTextFieldValues() {
let templateViews = super.templateViews
for view in templateViews {
if let textField = view as? NSTextField {
let text = textField.stringValue
print("Text in the texfield is: \(text)")
}
}
}
}
You can set the values the same way, by subclassing NSPredicateEditorRowTemplate and overriding the templateViews method.

How to toggle visibility of NSSplitView subView + hide Pane Splitter divider?

We have a parent Split view (NSSplitView), and two subviews, Content and SideBar (the sidebar is on the right).
What would be the optimal Cocoa-friendly way to toggle the SideBar view?
I would really love it, if the suggested solution includes animation
I really don't need any suggestions related to external plugins, etc (e.g. BWToolkit)
HINT : I've been trying to do that, but still I had issues hiding the divider of the NSSplitView as well. How could I do it, while hiding it at the same time?
Here's a pretty decent tutorial that shows how to do this: Unraveling the Mysteries of NSSplitView.
Hiding the divider is done in NSSplitView's delegate method splitView:shouldHideDividerAtIndex:.
You will have to animate the frame size change yourself if you don't like the way NSSplitView does it.
Easiest way to do it is as follows - and it's animated: [SWIFT 5]
splitViewItems[1].animator().isCollapsed = true // Show side pane
splitViewItems[1].animator().isCollapsed = false // hide side pane
I wrote a Swift version of the content in the link from #Nathan's answer that works for me. In the context of my example splitView is set elsewhere, probably as an instance property on an encompassing class:
func toggleSidebar () {
if splitView.isSubviewCollapsed(splitView.subviews[1] as NSView) {
openSidebar()
} else {
closeSidebar()
}
}
func closeSidebar () {
let mainView = splitView.subviews[0] as NSView
let sidepanel = splitView.subviews[1] as NSView
sidepanel.hidden = true
let viewFrame = splitView.frame
mainView.frame.size = NSMakeSize(viewFrame.size.width, viewFrame.size.height)
splitView.display()
}
func openSidebar () {
let sidepanel = splitView.subviews[1] as NSView
sidepanel.hidden = false
let viewFrame = splitView.frame
sidepanel.frame.size = NSMakeSize(viewFrame.size.width, 200)
splitView.display()
}
These functions will probably methods in a class, they are for me. If your splitView can be nil you obviously have to check for that. This also assumes you have two subviews and the one at index 1, here as sidePanel is the one you want to collapse.
In Xcode 9.0 with Storyboards open Application Scene select View->Menu->Show sidebar. CTRL-click Show Sidebar, in sent actions delete the provided one, click on x. From the circle CTRL drag to First Responder in application scene and select toggleSideBar to connect to. Open storyboard and select the first split view item and in attributes inspector change behaviour from default to sidebar. Run and try with view menu item show/hide. All done in interface builder no code. toggleSideBar handles the first split view item. https://github.com/Dis3buted/SplitViewController
I got some artifacts with the code above, likely because it was out of context. I am sure it works where it was meant to. Anyway, here is a very streamlined implementation:
// this is the declaration of a left vertical subview of
// 'splitViewController', which is the name of the split view's outlet
var leftView: NSView {
return self.splitViewController.subviews[0] as NSView
}
// here is the action of a button that toggles the left vertical subview
// the left subview is always restored to 100 pixels here
#IBAction func someButton(sender: AnyObject) {
if splitViewController.isSubviewCollapsed(leftView) {
splitViewController.setPosition(100, ofDividerAtIndex: 0)
leftView.hidden = false
} else {
splitViewController.setPosition(0, ofDividerAtIndex: 0)
leftView.hidden = true
}
}
To see a good example using animations, control-click to download this file.
If your NSSplitView control is part of a NSSplitViewController object, then you can simply use this:
splitViewController.toggleSidebar(nil)

How to use ActionSheet in ST2

I would like to use an actionsheet but am unclear where to place it. I have tried adding it to a button event function but it doesn't show (the modal screen does however). I get a message about ActionSheet#show showing a component that currently doesn't have any container. Please use Ext.Viewport.add() to add this component to the viewport. Not sure how to do that - using Ext.Viewport.add() doesn't work for me - i may be because of my layout which is:
I have a viewport controller/view which is a card layout. When I click a button I have a function in the viewport controller that loads a new controller/view card in the viewport. The actionsheet is in one of these cards. The app is to big to post so hopefully it makes sense.
I have tried adding the actionsheet in my view items array but do not know how to make it show - making a reference to the xtype actionsheet doesn't return an object with a show() method it seems.
Edit: after more experiments it seems the issue is that I am placing it inside of a card - the card layout container has a relative position and the actionsheet absolute - somehow this is causing the actionsheet to go off screen. Setting card container to absolute fixes it but now I have problems with navbar positions. Suggestions?
So a bit stuck...
This is what you need to do to show your action sheet :
var actionSheet = Ext.create('Ext.ActionSheet', {
items: [
{
text: 'Delete draft',
ui : 'decline'
},
{
text: 'Save draft'
},
{
text: 'Cancel',
ui : 'confirm'
}
]
});
Ext.Viewport.add(actionSheet);
actionSheet.show();