Swift Cocoa manipulate design of titlebar buttons - swift

I'm currently developing a Swift/Cocoa application with XCode 12.3 and Swift 5.
I'd like to manipulate the design of the close button in the titlebar of a NSWindow.
I've already tried to change the design using
self.window?.standardWindowButton(.closeButton)?.layer. ... = ...
But this approach seems not to be working - at least for me. Do you have any ideas on how to make it possible?

You need to set wantsLayer to true. Standard window buttons are regular NSButtons with an NSCell inside them. To customize the and not have the original button block the view, you either need to remove the cell and use the layer or just create a custom one.
Note that when removing or setting your own button cell, its action selector will reset, so you need to keep sure that the buttons still do what you want them to.
Here's the code in Objective C, but it should be easy enough to translate into Swift:
NSButton *button = [self standardWindowButton:NSWindowCloseButton];
button.wantsLayer = YES;
button.layer.backgroundColor = NSColor.greenColor.CGColor;
NSCell *cell = [[NSCell alloc] initTextCell:#"?"];
[button setCell:cell];
Result:

Related

Swift UIKit: Unable to update UIButton X when pressing UIButton Y [duplicate]

I was trying to make a simple tic-tac-toe app in Swift, so I set up 9 buttons with tags from 1 to 9 and call setImage to set noughts or crosses. This is working as intended.
The problem comes when trying to reset the board, where I call this piece of code:
for i in 1..<10 {
if let button = view.viewWithTag(i) as? UIButton {
button.setImage(nil, for: .normal)
}
}
This should remove the image from the button, but it does nothing. The button is set to Custom in the storyboard, and the tags are indeed assigned as they should. I also tried getting an outlet for one of the buttons and calling setImage(nil, for: .normal) in that one and it didn't work either.
I even created a new project with just a button where I call setImage, and it is indeed working for non-nil images but not with nil as value.
Has Apple changed the way of removing images? Another question(When button pressed, button.setImage(nil) doesn't work) seems to have the same problem, but if I work with isHidden I can no longer click on the buttons, and it should not be the workaround, looks very hacky to me.
Is this a bug on iOS? Did the implementation change or something?
Thank you.
This seems like it may be a bug in iOS 15, but it still works if use the new UIButton Configuration API you set its configuration's image instead:
button.configuration?.image = nil
Be sure to also configure your button's image through its configuration property when you want it to have an image on iOS 15, too

Overwriting UINavigationBar to set a default titleView

What I want to do is a navigation bar with a image on it. I have a tab controller on my main view, and inside each tab I have a UINavigationController. From inside the UIViewController that my tab/navigationController calls, I could set the titleView without much problem, doing this inside the viewDidLoad method:
self.navigationItem.titleView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"mylogo.png"]] autorelease];
But, I want to replace all titles in my navigationBar for this view, and it seems ugly to repeat this everywhere. So I did this on the delegate (after linking all the Outlet stuff)
self.tabOneNavController.navigationBar.topItem.titleView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"mylogo.png"]] autorelease];
Again, it worked! ok, I'm almost getting there.
But the point is, I've 5 tabs and all of them have navigationControllers inside. I reduced the code repetition from every internal view to only 5 times, but it still. It requires that I do that for the NavController of each tab.
Then I tried to extend the UINavigationBar to create my own, where I could set this in the initializer, and use it in the interface builder as the object class. But it doesn't seem to work. Here is what I did:
#implementation MyNavigationBar
- (id)init {
self = [super self];
self.tintColor = [UIColor greenColor];
self.topItem.title = #"testing please work";
return self;
}
#end
in the interface file MyNavigationBar inherits from UINavigationBar. But this didn't work. Should I overwrite other method? which one? is this a good practice?
I'm not even sure if I should add one navigationBar for each tab, as I said, I have tabs and I want to have a navigation bar / navigate inside them. By now, after a near death experience trying to figure out how the interface builder / outlets and classes work, the code is working, I just would like to make unglify it.
Thank you!
The problem of repeating code which you describe has an elegant solution. Objective-C supports something called a "category", which allows you to add methods to a class. A common use for this is to customize navigation and tab bars. In Xcode 4, you would do something like this to add a category on UINavigationBar:
Hit Command+N or open the "New File" dialog. Next, choose "Objective-C category" from the Cocoa Touch menu:
Click Next and you will be prompted to enter the name of the class that you would like to add methods to as a category. It should look something like this:
Then, you should end up with a save file dialog. A quick note about convention here. Convention is to name a category after the original class, the plus sign, and then a description of what you're adding. Here's what yours might look like:
Once you save your file, you will need get something like this:
Look at that beauty. You can now override the default drawing/init methods as well as extend the functionality of the navbar.
I'd suggest looking into the init and drawRect methods, although I don't remember which ones people use. Also, please note that while under NDA, this may change in iOS 5, so just be prepared for that possibility.
Why not define a UIViewController subclass which sets the title view via self.navigationItem.titleView and have your other view controllers extend from that class? Then you're sharing that behavior across all of your controllers without repeating the implementation.

Edit a UIViewController

I can't seem to find any information on what should be a simple issue. I have a table which contains a series of cells. When you tap on the cell it shows the information of that object in detail. I know it is possible to have a button which places all of the data on the screen in EDIT mode. Any tutorials or advice as how to do this (properly/ with best practices)?
Just to be clear this is for iPhone/ Objective-C/ Cocoa.
Thanks,
EDIT 1
Sorry. I know how to put the button there. But how do make the labels editable?
You are confusing two states:
Putting the tableView into editing mode - this is for deleting, or moving cells around in the table, regardless of the cell content. This is controlled by the UITableView.editing property.
and
Putting the tableViewCell into some sort of editing state. There is no official editing state for the cell (i.e. there is no single flag to set to make all UILabels in a cell into editable textFields.) You need to implement all of this logic yourself. If you're using .xibs, a good practice here is to have a different .xib for your cell's editing mode.
You cannot have editable labels. However, you can replace the label with a textfield when the button is pressed, and then update the label once finished.
One way to do this is the following. Create a textfield in the same location as the label and initially set textField.hidden = YES;. Then implement something along these lines:
-(IBAction)editMyCell:(id)sender {
textField.text = cellLabel.text;
cellLabel.hidden = YES;
textField.hidden = NO;
[textField becomeFirstResponder];
}
and when the editing has finished, restore with
-(void)textFieldDidEndEditing:(UITextField *)textField {
cellLabel.text = textField.text;
textField.hidden = YES;
cellLabel.hidden = NO;
[textField resignFirstResponder];
}
You'll probably want to tweak this idea a bit for your situation, but it's probably the simplest thing to implement that achieves what you're after.
There is nothing in the base sdk associated with editing a UIViewController. Normally that kind of logic is defined by the programmer. But I could see someone writing a function that turns all of your UILabels in your UIView into UITextViews so the user can edit the text.
There might be sample code out there but this seems like custom code to me.

Making a custom Button using a UIView or overriding UIButton?

I need to generate a custom button through code, this is how i am currently doing it.
-(void) initialiseButtons
{
int ypos = playerImage.frame.origin.y + playerImage.frame.size.height + 8;
for(int i=0; i<totalButtons; i++)
{
UIButton *newButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[newButton setFrame:CGRectMake(20, ypos, 220, 30)];
newButton.tag = 10 + i;
[newButton addTarget:self action:#selector(statTapped:) forControlEvents:UIControlEventTouchUpInside];
[self.frontView addSubview:newButton];
ypos += 30 + 7;
}
}
This creates the blank buttons perfectly through code, gives them a tag and assigns an callback function on touchUpInside.
The custom button needs to be able to handle showing an image when pressed down.
It needs to be able to draw 2 pieces of Text. 1 aligned to the left hand side of the button and 1 aligned to the righthand side of the button.
My boss suggested instead of buttons I use a View. I dont understand how this will work. When i start thinking about it, i think it would require having a viewcontroller dedicated to the buttons. And some draw method? It sounds complicated and I am not grasping how it can be done.
Is there a simpler method by making a custom class overriding UIButton? I made a simple test class earlier but nothing was drawn in the buttons locations when I used them in place of the Normal UIButton class. I expected i would see the buttonUp.png drawn.
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#interface CardButton : UIButton {
}
#end
#import "CardButton.h"
#import <UIKit/UIKit.h>
#implementation CardButton
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame])
{
[self setImage:[UIImage imageNamed:#"buttonUp.png"] forStates:UIControlStateNormal];
self.adjustsImageWhenHighlighted = YES;
}
return self;
}
#end
Anybody able to give me some pointers on this? I'm pretty blocked at the moment. Have been reading some various other threads related to buttons but nothing that made it clear in my head how to tackle the problem
Personally, I would subclass UIButton. Despite all the talk that you can't subclass UIButton, Apple even talks about subclassing UIButton in the UIButton documentation.
In the subclass I would create a UIView with the two labels (or images or whatever) and add them as subviews to the button (be sure to set interactive for the text and view as FALSE).
What is awesome about this is that it leverages the UIButton code and keeps you from reinventing the wheel.
You should not (I'd almost say you can't) subclass UIButton, as it turns out to be a class cluster, and it would be impractical (read: impossible) to derive from that.
So your two options are:
1) Use standard UIButton of custom type, add the elements you want to show (i.e. UILabel) and hook up actions for touch down and touch up and react accordingly (change the views, trigger actions etc.)
2) Use a custom UIView, implement drawRect: to draw how you like it or use custom views as subviews as in 1). Then use the touchesBegan:, touchesEnded: etc. messages to react accordingly (or UIGestureRecognizer if you can live with 3.2+ compatibility).
You might build a factory to build those buttons if they all are very similar, so that your code becomes clean and non-repetive.
Edit as per iOS 6:
Apple now mentions subclassing in the UIButton docs, so while still not very well defined, it seems quite safe now to do so.
An easy way to do this, is to override a UIView. In the view you add a UIButton as Subview. This way you have a reusable class without the need to re-implement button behaviour.
You can style your button the way you want in the initWithFrame method of your UIView derived class. If you use this class as target for your button events, you can implement special behaviour easily, like showing the image.
For the two pieces of text, you create two labels and add them as subviews to the button.

iPhone Checkboxes à la Mail

After reading the iPhone Human Interface Guidelines, I notice there's little mention of checkboxes in the style that one may encounter them on the desktop or web.
Checkboxes are generally handled by UISwitchs on the iPhone, but for an app I'm presently working on, they're really not the right control. Instead, the control you'll see in Mail is a much better fit:
Actual mail blanked out. Obviously.
How would I go about using these checkbox controls in my app? Are they standard, or will I need to imitate them with a custom control?
Cheers friends.
You'll need to create a custom control. It won't be difficult since UIControl already has 'selected', 'highlighted' and 'state' properties at your disposal. You'll just need to draw and toggle appropriately.
Don't subclass UIControl. What you want is a UIButton of "custom" type. Load it with your "unlit" image in IB (or programmatically in -viewDidLoad--you can set it appropriate to its data there too, if you came here with that property already "checked").
Point its touchUpInside event at a method called -(void)toggleCheckBox, and in that method, toggle whatever setting you're toggling (probably a BOOL property of the objects you're listing), and toggle the "lit/unlit" status of the button image by using its -setImage: forState: method. Use the control state UIControlStateNormal.
I do something similar where I let people poke a button to toggle the "favorite" status of the thing ("thisEvent"--a member of an array of local cultural/arts events) they're looking at:
- (IBAction)toggleFavorite {
if (self.thisEvent.isFavorite == YES) {
self.thisEvent.isFavorite = NO;
[self.favoriteButton setImage:[UIImage imageNamed:#"notFavorite.png"] forState:UIControlStateNormal];
}
else {
self.thisEvent.isFavorite = YES;
[self.favoriteButton setImage:[UIImage imageNamed:#"isFavorite.png"] forState:UIControlStateNormal];
}
}
I'm pretty certain there is no standard way to do this. However it's fairly simple to achieve, all you need is two images, one for each state. I would probably do something simple like subclass UIImageView and add a setState:(BOOL)theState method, which would then simply select the relevant image.
I'd rather subclass UITableViewCell then UIImageView. UITableViewCell allready comes with selected/unselected states and handlers for editmodes etc.
As said before, you'll need to subclass UIControl. The actual process was discussed here w little while ago.
I also found a description of another way to do this using the same image/method that the Mail app uses:
http://networkpx.blogspot.com/2009/07/multiple-row-selection-with-uitableview.html
but as this implements undocumented features of the iOS SDK, it may not be best for apps intended for the official App Store.