NSSearchField: How to hide icon and border? - swift

This is kind of a duplicate of this question. Because everything I know about Swift is Swift3, I`m wondering if someone could "translate" the suggested solution in this answer.
Also:
I made a NSSearchfield without border, put it in a framed view, and it still shows the gray border. I would be curious of how to disable the animated gray border and maybe even how to change the color of the gray "search" line.
My ugly result now looks like this:
It would be a big help if someone could tell me how to manage this difficult NSSearchfield.
//UPDATE
According to firstinq´s answer, the icon now disappeared, which is great. But still, there is this disturbing animated gray border. Which I can´t understand: The NSSearchFielt is inside a NSView (blue border). So everything outside the NSView should be hidden, right?. So why am I still seeing the gray border? cell.isBordered = falsehas no effect.
Any advice how to handle that?
This is how I draw the border of the NSView:
class SearchFieldBorder: NSView {
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
self.layer?.borderWidth = 1
self.layer?.borderColor = NSColor.blue.cgColor
}
}

To hide the icon: cast the cell to NSSearchFieldCell and set the cell's searchButtonCell to transparent. Possible swift3 version:
if let cell = self.searchField.cell as? NSSearchFieldCell {
cell.searchButtonCell?.isTransparent = true
}
Here searchField is an NSSearchField
To remove the focus border:
searchField.focusRingType = .none
To change grey line/cursor it would be better to subclass the NSSearchField and override the methods.
You can get an idea from here.

I'll supplement the answer above.
To hide the search icon, assign a nil to the SearchButtonCell property
if let cell = searchField.cell as? NSSearchFieldCell {
cell.searchButtonCell = nil
}

Related

How can I lower the view into the safe area?

I'm practicing using snapkit to place ui of view.
However, I tried many things to move the red box into the safe area under the notch, but I couldn't find a way.
var redView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(redView)
redView.backgroundColor = .red
redView.snp.makeConstraints{ make in
make.top.equalTo(view.safeAreaInsets.top)
make.size.width.height.equalTo(100)
make.left.equalTo(view.snp.left)
}
here is my code.
Why doesn't it still come into the safe area even if I designate the red box tower as Safe Area Insets.top?
I would appreciate it if you could let me know my mistake.
Try this:
redView.snp.makeConstraints{ make in
make.top.equalTo(view.safeAreaLayoutGuide.snp.top)
make.size.width.height.equalTo(100)
make.left.equalTo(view.snp.left)
}

How can I round the corners of only one side of an NSButton in Swift 4?

I am new to Swift and Stack Overflow in general, so I hope I'm not asking much to bear with me.
I am trying to achieve a 'grouped' button style that can be found on Finder or in the XCode editor toolbar, like these two button groups. As you can see in the first group of buttons, the left button is only rounded on the left side, the centre button is not rounded at all, and the right button is only rounded on the right side. The same thing applies to the second group of buttons. I want to accomplish something like this, but I'm unsure of how to achieve this.
After searching for a solution online (including iOS tutorials), I tried providing an extension to the NSButton class and manually rounding the two left corners like so:
// Extensions.swift
extension NSButton {
func roundLeftCorners() {
self.layer?.maskedCorners = [.layerMinXMinYCorner, .layerMinXMaxYCorner]
self.layer?.cornerRadius = 20.0 // Some arbitrary number, just wanted to make the rounded corner visible
self.layer?.masksToBounds = true
}
}
Then, on my view controller's viewDidLoad() function, I tried calling this member:
// MyViewController.swift
class MyViewController: NSViewController {
#IBOutlet weak var leftButton: NSButton!
override func viewDidLoad() {
super.viewDidLoad()
leftButton.roundLeftCorners()
}
// ...
}
...but that didn't work for me. Some simple debugging showed that the Optional values of self.layer were nil, so I'm not sure what's going on there.
Next, I tried creating my own custom class and overriding the draw(_ dirtyRect:) function with the same code above, like so:
// LeftButton.swift
class LeftButton: NSButton {
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
self.layer?.maskedCorners = [.layerMinXMinYCorner, .layerMinXMaxYCorner]
self.layer?.cornerRadius = 20.0
self.layer?.masksToBounds = true
}
}
// MyViewController.swift
class MyViewController: NSViewController {
#IBOulet weak var leftButton: LeftButton!
// ...
}
...but that didn't remove the rounded corners on the right side. Weirdly enough, the new cornerRadius value is only obvious if the number is around 50.0 or greater; any less and the left corners looks exactly the same as any other NSButton.
Some answers mentioned manually drawing the points in a path with NSBezierPath, but it doesn't achieve what I want. I also can't find any related properties/attributes on the Storyboard editor. Perhaps I've overcomplicated my approach to this seemingly easy problem, or maybe I'm not looking at it the right way, but I hope someone could help me with this. Thanks in advance!
1) The images you showed are using a simple NSSegmentedControl. Nothing needs customized.
2) What you tried to do wouldn't work anyway; If it could mechanically work, what it would end up doing is merely clipping the drawn content on the left corners. It wouldn't magically fill in drawing on the right, and create appropriate control borders etc.
AppKit controls are not merely CALayers with filled in properties like border, background, etc. They are almost all entirely drawn using Core Graphics via the classic drawRect: method one way or another. The fact that views have a layer is due to layer-backing. There are very few things you can end up doing with the layer of an existing control. To customize them properly, you would override the standard drawing routines in NSView, NSControl, NSCell, etc as appropriate.

Swift - How to add a layer on a UIImageView inside of a UICollectionViewCell and keep the parallax effect

I'm working on a tvOS project. This project contains a UICollectionView with a UIImageView inside of each UICollectionViewCell. First of all I'm using adjustsImageWhenAncestorFocused on the image, because it looks good when the cell is in focus and I want to have the 'parallax' effect.
When a cell is in focus there should pop up an label over the image(for the title of a product) which is not a problem to make. But I want the title to be good readable so thats why I want to put on a gradient layer on top of the image.
The problem:
When I add the gradient layer to the UIImageView, it goes on top of it but it doesn't 'stick' to it. It should go with the image when the user is doing the parallax thing, but it stays at its position and the original image does move underneath the gradient layer
Screenshot of the problem
The code
let gradientLayer = CAGradientLayer()
override func didUpdateFocus(in context: UIFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) {
gradientLayer.colors = [UIColor(red:0.00, green:1.00, blue:1.00, alpha:0.3).cgColor, UIColor(red:1.00, green:0, blue:1.00, alpha:0.3).cgColor]
gradientLayer.frame = productImageView.focusedFrameGuide.layoutFrame
gradientLayer.isHidden = true
self.productImageView.layer.addSublayer(self.gradientLayer)
if (self.isFocused){
gradientLayer.isHidden = false
}
else {
gradientLayer.isHidden = true
}
}
Additional question
Is this a bug? Because I still don't figured it out how to fix this problem. Should I give this to Apple?
Apple introduced overlayContentView in tvOS 11. You should add your label to this view and it will do the focus for you!
For more information read https://developer.apple.com/documentation/uikit/uiimageview/2882128-overlaycontentview?changes=latest_minor

how to customize scroll color of tableview in iphone sdk?

I want to set scroll style to pink color in tableview.please give any suggestion for that.
but only available from this colors
[tableview setIndicatorStyle:UIScrollViewIndicatorStyleWhite];
Replace UIScrollViewIndicatorStyleWhite to anything from this typedef.
typedef enum {
UIScrollViewIndicatorStyleDefault,
UIScrollViewIndicatorStyleBlack,
UIScrollViewIndicatorStyleWhite
} UIScrollViewIndicatorStyle;
For the moment is not possible set a custom color in the Scroll. the only posible colors are white and black.
http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIScrollView_Class/Reference/UIScrollView.html#//apple_ref/doc/uid/TP40006922-CH3-SW5
This code works for me, Swift-3
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
let verticalIndicator = scrollView.subviews.last as? UIImageView
verticalIndicator?.image = nil
verticalIndicator?.backgroundColor = UIColor.red
}

NSScrollView with unclipped content view?

Is there a way I can set my scrollview not to clip its contents? (Which is a NSTextView)
I have a subclass of NSScrollView and want its content not to be clipped to its bounds.
I have tried overriding:
- (BOOL) wantsDefaultClipping{
return NO;
}
in MyScrollView and in MytextView without any effect.
In the iOS I would simply would do: myuitextView.clipsToBounds=NO; how can I do this in Cocoa?
EDIT
This is an example of what I want to achieve but in the mac
The scrollview is white, the scroller will never go outside its bounds but the text does since I did myuitextView.clipsToBounds=NO
See picture here
EDIT2
I wouldn't mind clip my view like #Josh suggested. But the real behaviour I would like to have can be explained with this picture:
Do you see the word *****EDIT***** that has being cut in the very first line?
I want the text not to be cut this way, rather I want it to completely appear and I will put a semitransparent image so it looks like it fades off when it's outside the frame.
Q: Why don't I simply put a semitransparent NSImageView on it so it looks like what I want?
A: Because 1.Scroller will be faded as well. Even if I correctly place the semitransparent NSImageView so the scroller looks fine, the cursor/caret will be able to go underneath the semitransparent NSImageView again it does not look good.
I would like to be able to control the area is clipped by NSClipView. I think that would solve my problem. Is there any alternative I have? maybe I can control the caret position or scrolling position through NSTextView so caret will never go near the top/bottom frame limits? or any work-around?
Any advice is appreciated.
Now that it's 2016 and we're using vibrant titlebars with full size content views, I'll add my thoughts to how someone might accomplish this. Hopefully, this will help anyone who came here looking for help on this, as it helped me.
This answers the question in regards to scrolling under the titlebar, but you could easily modify this technique to scroll under other things using the insets and caret position.
To get a scroll view (with or without an NSTextView inside of it) to scroll behind a titlebar, you can use:
// For transparent title.
window.titlebarAppearsTransparent = true
window.styleMask = window.styleMask | NSFullSizeContentViewWindowMask
window.appearance = NSAppearance(named: NSAppearanceNameVibrantLight)
This effectively overlays the titlebar of the NSWindow onto the window's contentView.
To constrain something to the top of the window without knowing the height of the titlebar:
// Make a constraint for SOMEVIEW to the top layout guide of the window:
let topEdgeConstraint = NSLayoutConstraint(
item: SOMEVIEW, attribute: NSLayoutAttribute.Top,
relatedBy: NSLayoutRelation.Equal,
toItem: window.contentLayoutGuide,
attribute: NSLayoutAttribute.Top, multiplier: 1.0, constant: 0.0)
// Turn the constraint on automatically:
topEdgeConstraint.active = true
This allows you to constrain the top of an element to the bottom of the titlebar (and or toolbar + any accessory views it may have). This was shown at WWDC in 2015: https://developer.apple.com/videos/play/wwdc2014/220/
To get the scrollview to scroll under the titlebar but show its scrollbars inside the unobscured part of the window, pin it to the top of the content view in IB or via code, which will cause it to be under the titlebar. Then, tell it to automatically update it's insets:
scrollView.automaticallyAdjustsContentInsets = true
Finally, you can subclass your window and handle the cursor/caret position. There is a presumed bug (or developer error on my part) that doesn't make the scrollview always scroll to the cursor/caret when it goes above or below the content insets of the scrollview.
To fix this, you must manually find the caret position and scroll to see it when the selection changes. Forgive my awful code, but it seems to get the job done. This code belongs in an NSWindow subclass, so self is referring to the window.
// MARK: NSTextViewDelegate
func textViewDidChangeSelection(notification: NSNotification) {
scrollIfCaretIsObscured()
textView.needsDisplay = true // Prevents a selection rendering glitch from sticking around
}
// MARK: My Scrolling Functions
func scrollIfCaretIsObscured() {
let rect = caretRectInWindow()
let y: CGFloat = caretYPositionInWindow() - rect.height
// Todo: Make this consider the text view's ruler height, if present:
let tbHeight: CGFloat
if textView.rulerVisible {
// Ruler is shown:
tbHeight = (try! titlebarHeight()) + textViewRulerHeight
} else {
// Ruler is hidden
tbHeight = try! titlebarHeight()
}
if y <= tbHeight {
scrollToCursor()
}
}
func caretYPositionInWindow() -> CGFloat {
let caretRectInWin: NSRect = caretRectInWindow()
let caretYPosInWin: CGFloat = self.contentView!.frame.height - caretRectInWin.origin.y
return caretYPosInWin
}
func caretRectInWindow() -> CGRect {
// My own version of something based off of an old, outdated
// answer on stack overflow.
// Credit: http://stackoverflow.com/questions/6948914/nspopover-below-caret-in-nstextview
let caretRect: NSRect = textView.firstRectForCharacterRange(textView.selectedRange(), actualRange: nil)
let caretRectInWin: NSRect = self.convertRectFromScreen(caretRect)
return caretRectInWin
}
/// Scrolls to the current caret position inside the text view.
/// - Parameter textView: The specified text view to work with.
func scrollToCursor() {
let caretRectInScreenCoords = textView.firstRectForCharacterRange(textView.selectedRange(), actualRange: nil)
let caretRectInWindowCoords = self.convertRectFromScreen(caretRectInScreenCoords)
let caretRectInTextView = textView.convertRect(caretRectInWindowCoords, fromView: nil)
textView.scrollRectToVisible(caretRectInTextView)
}
enum WindowErrors: ErrorType {
case CannotFindTitlebarHeight
}
/// Calculates the combined height of the titlebar and toolbar.
/// Don't try this at home.
func titlebarHeight() throws -> CGFloat {
// Try the official way first:
if self.titlebarAccessoryViewControllers.count > 0 {
let textViewInspectorBar = self.titlebarAccessoryViewControllers[0].view
if let titlebarAccessoryClipView = textViewInspectorBar.superview {
if let view = titlebarAccessoryClipView.superview {
if let titleBarView = view.superview {
let titleBarHeight: CGFloat = titleBarView.frame.height
return titleBarHeight
}
}
}
}
throw WindowErrors.CannotFindTitlebarHeight
}
Hope this helps!
I would simply try to observe the document view's frame and match the scroll view's frame when the document resizes.
This is a little hairy. AFAIK, NSViews can't draw outside their own frame. At any rate I've never seen it done, and I was somewhat surprised when I realized that UIView allows it by default. But what you probably want to do here is not manipulate clipping rectangles (doing any such thing inside NSScrollView will probably not do what you want or expect), but instead try to cover up the vertically-truncated text lines with either layers or views that are the same color as the background. Perhaps you could subclass NSClipView and override viewBoundsChanged: and/or viewFrameChanged: in order to notice when the text view is being shifted, and adjust your "shades" accordingly.
You might consider using a translucent layer to achieve this appearance, without actually drawing outside your view. I'm not certain of the rules on iOS, but on the Mac, a view drawing outside its bounds can cause interference with surrounding drawing.
However, you can set the clipping region to be whatever you like inside your scroll view subclass's drawRect: using -[NSBezierPath setClip:]:
- (void)drawRect:(NSRect)dirtyRect {
[NSGraphicsContext saveGraphicsState];
[[NSBezierPath bezierPathWithRect:[[self documentView] frame]] setClip];
//...
[NSGraphicsContext restoreGraphicsState];
}
It might be possible (since you asked) to use this code in an NSClipView subclass, but there's not much info about that, and I think you may have a hard time making it interact properly with its scroll view. If it were me, I'd try subclassing NSScrollView first.