11

I have classic TableView where you can delete item if you swipe and than clicking on the button. I know how to set custom background on the cell, but I can't find how I can set custom font and color for that.

Thank you for help!

func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [AnyObject]?  {

    var deleteAction = UITableViewRowAction(style: UITableViewRowActionStyle.Default, 
                   title: "Delete", 
                   handler: { 
                      (action:UITableViewRowAction!, indexPath:NSIndexPath!) -> Void in
                           println("Delete button clicked!")
                   })

    deleteAction.backgroundColor = UIColor.redColor()

    return [deleteAction]
}
ipinak
  • 5,739
  • 3
  • 23
  • 41
Vlastimil Fiser
  • 177
  • 1
  • 2
  • 9

9 Answers9

11

Well, the only way I've found to set a custom font is to use the appearanceWhenContainedIn method of the UIAppearance protocol. This method isn't yet available in Swift, so you have to do it in Objective-C.

I made a class method in a utility Objective-C class to set it up:

+ (void)setUpDeleteRowActionStyleForUserCell {

    UIFont *font = [UIFont fontWithName:@"AvenirNext-Regular" size:19];

    NSDictionary *attributes = @{NSFontAttributeName: font,
                      NSForegroundColorAttributeName: [UIColor whiteColor]};

    NSAttributedString *attributedTitle = [[NSAttributedString alloc] initWithString: @"DELETE"
                                                                          attributes: attributes];

    /*
     * We include UIView in the containment hierarchy because there is another button in UserCell that is a direct descendant of UserCell that we don't want this to affect.
     */
    [[UIButton appearanceWhenContainedIn:[UIView class], [UserCell class], nil] setAttributedTitle: attributedTitle
                                                                                          forState: UIControlStateNormal];
}

This works, but it's definitely not ideal. If you don't include UIView in the containment hierarchy, then it ends up affecting the disclosure indicator as well (I didn't even realize the disclosure indicator was a UIButton subclass). Also, if you have a UIButton in your cell that is inside a subview in the cell, then that button will get affected by this solution as well.

Considering the complications, it might be better to just use one of the more customizable open source libraries out there for table cell swipe options.

Logan Gauthier
  • 393
  • 2
  • 6
  • 4
    I have figured out an improvement on this answer to avoid affecting other buttons inside the cell. After inspecting the view hierarchy of an expanded cell, I found that the delete button is sitting inside a `UITableViewCellDeleteConfirmationView` view. This is a private API but using this instead of `UIView` targets only the delete button and not any other button in the cell. So instead of `[UIView class]`, use `[NSClassFromString(@"UITableViewCellDeleteConfirmationView") class]` – Dima May 13 '15 at 23:50
  • @Dima Can you confirm if your app passed the review, or they rejected it because you used the private API? – Septronic Jan 02 '16 at 11:09
  • This was a while ago and my app has passed several reviews without a problem. – Dima Jan 04 '16 at 22:07
  • How to add more buttons in this way? – guhan0 Jul 10 '17 at 12:38
  • After doing a `grep` following this https://nshipster.com/uiappearance/, doesn't seem that `setAttributedTitle:forState:` is available for `UIButton` customization. Can you confirm that this answer still works? – henrique Mar 18 '19 at 12:01
  • 2
    does not work for me, at least on iOS 12. The button is contained in a private class view called `UISwipeActionPullView` so you have to use `NSClassFromString` which should be avoided – user2378197 Sep 20 '19 at 11:07
4

I want to share my solution for ObjC, this is just a trick but works as expected for me.

- (NSArray<UITableViewRowAction *> *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // this just convert view to `UIImage`
    UIImage *(^imageWithView)(UIView *) = ^(UIView *view) {

        UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.opaque, 0.0);
        [view.layer renderInContext:UIGraphicsGetCurrentContext()];
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return image;
    };

    // This is where the magic happen,
    // The width and height must be dynamic (it's up to you how to implement it)
    // to keep the alignment of the label in place
    //
    UIColor *(^getColorWithLabelText)(NSString*, UIColor*, UIColor*) = ^(NSString *text, UIColor *textColor, UIColor *bgColor) {

        UILabel *lbDelete = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 47, 40)];
        lbDelete.font = [UIFont boldSystemFontOfSize:11];
        lbDelete.text = text;
        lbDelete.textAlignment = NSTextAlignmentCenter;
        lbDelete.textColor = textColor;
        lbDelete.backgroundColor = bgColor;

        return [UIColor colorWithPatternImage:imageWithView(lbDelete)];
    };

    // The `title` which is `@"   "` is important it 
    // gives you the space you needed for the 
    // custom label `47[estimated width], 40[cell height]` on this example
    //
    UITableViewRowAction *btDelete;
    btDelete = [UITableViewRowAction
                rowActionWithStyle:UITableViewRowActionStyleDestructive
                title:@"   "
                handler:^(UITableViewRowAction * _Nonnull action, NSIndexPath * _Nonnull indexPath) {
                    NSLog(@"Delete");
                    [tableView setEditing:NO];
                }];
    // Implementation
    //
    btDelete.backgroundColor = getColorWithLabelText(@"Delete", [UIColor whiteColor], [YJColor colorWithHexString:@"fe0a09"]);

    UITableViewRowAction *btMore;
    btMore   = [UITableViewRowAction
                rowActionWithStyle:UITableViewRowActionStyleNormal
                title:@"   "
                handler:^(UITableViewRowAction * _Nonnull action, NSIndexPath * _Nonnull indexPath) {
                    NSLog(@"More");
                    [tableView setEditing:NO];
                }];
    // Implementation
    //
    btMore.backgroundColor = getColorWithLabelText(@"More", [UIColor darkGrayColor], [YJColor colorWithHexString:@"46aae8"]);

    return @[btMore, btDelete];
}

[YJColor colorWithHexString:<NSString>]; is just to convert hex string to UIColor.

Check the example output screenshot.
enter image description here

0yeoj
  • 4,500
  • 3
  • 23
  • 41
2

If you use XCode's Debug View Hierarchy to look what is happening in UITableView when the swipe buttons are active, you'll see that UITableViewRowAction items translates to button called _UITableViewCellActionButton, contained in UITableViewCellDeleteConfirmationView. One way to change button's properties is to intercept it when it's added to UITableViewCell. In your UITableViewCell derived class write something like this:

private let buttonFont = UIFont.boldSystemFontOfSize(13)
private let confirmationClass: AnyClass = NSClassFromString("UITableViewCellDeleteConfirmationView")!

override func addSubview(view: UIView) {
    super.addSubview(view)

    // replace default font in swipe buttons
    let s = subviews.flatMap({$0}).filter { $0.isKindOfClass(confirmationClass) }
    for sub in s {
        for button in sub.subviews {
            if let b = button as? UIButton {
                b.titleLabel?.font = buttonFont
            }
        }
    }
}
apetrovic
  • 51
  • 3
  • 1
    Just a note: This uses a private API. It may get rejected, or you may have luck. – Frederik Winkelsdorf Apr 24 '16 at 16:27
  • Don't know if this changed for iOS 11 or did in 10... but it looks like that's no longer the class getting added. In Xcode 9 I'm seeing a UISwipeActionStandardButton. As is with attempting to use Private API stuff, the min Apple changes it, your stuff no longer works. – jhelzer Jun 24 '17 at 00:35
0

This seems to work, at least for setting the font color:

- (void)setupRowActionStyleForTableViewSwipes {
    UIButton *appearanceButton = [UIButton appearanceWhenContainedInInstancesOfClasses:@[[NSClassFromString(@"UITableViewCellDeleteConfirmationView") class]]];
    [appearanceButton setTitleColor:[UIColor lightGrayColor] forState:UIControlStateNormal];
}
Alexandru
  • 12,264
  • 17
  • 113
  • 208
0

You could use UIButton.appearance to style the button inside the row action. Like so:

let buttonStyle = UIButton.appearance(whenContainedInInstancesOf: [YourViewController.self])

let font = UIFont(name: "Custom-Font-Name", size: 16.0)!
let string = NSAttributedString(string: "BUTTON TITLE", attributes: [NSAttributedString.Key.font : font, NSAttributedString.Key.foregroundColor : UIColor.green])

buttonStyle.setAttributedTitle(string, for: .normal)

Note: this will affect all of your buttons in this view controller.

Faipdeoiad
  • 102
  • 2
  • 3
  • Thank you for this code snippet, which might provide some limited, immediate help. A [proper explanation](https://meta.stackexchange.com/q/114762/349538) would greatly improve its long-term value by showing why this is a good solution to the problem and would make it more useful to future readers with other, similar questions. Please [edit] your answer to add some explanation, including the assumptions you’ve made. – Dwhitz Mar 27 '19 at 14:17
-1

Here is some Swift Code that might be helpful:

func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) ->[AnyObject]? {

let attributes = [NSFontAttributeName: UIFont.systemFontOfSize(UIFont.systemFontSize())] as Dictionary!
UIButton.appearance().setAttributedTitle(NSAttributedString(string: "Your Button", attributes: attributes), forState: .Normal)

// Things you do...

}

This will manipulate all buttons in your application.

alexdd55
  • 1,109
  • 15
  • 24
  • 4
    Actually this will manipulate ALL buttons throughout your application. It appears that after this code is executed all buttons in the application are affected by this code. Not an ideal solution unless you don't have any other buttons in your application, which is probably not the case. – northdig Aug 12 '15 at 22:20
-1

I think you can use this method to change the appearance only in one (or more, you can define it) viewcontrollers:

    //create your attributes however you want to
    let attributes = [NSFontAttributeName: UIFont.systemFontOfSize(UIFont.systemFontSize())] as Dictionary!            

   //Add more view controller types in the []
    UIButton.appearanceWhenContainedInInstancesOfClasses([ViewController.self])

Hope this helped.

Septronic
  • 1,156
  • 13
  • 32
-1
//The following code is in Swift3.1
    func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]?
    {
        let rejectAction = TableViewRowAction(style: UITableViewRowActionStyle.default, title: "\u{2715}\nReject") { action, indexPath in
                print("didtapReject")
            }
            rejectAction.backgroundColor = UIColor.gray
            let approveAction = TableViewRowAction(style: UITableViewRowActionStyle.default, title: "\u{2713}\nApprove") { action, indexPath in
                print("didtapApprove")
            }
            approveAction.backgroundColor = UIColor.orange
            return [rejectAction, approveAction]
       }
Nrv
  • 280
  • 1
  • 6
-18

How to use a custom font?

It's pretty easy.

  1. Firstly, you need to include your custom font files to your project.
  2. Next, go to your info.plist file and add a new entry with the key "Fonts provided by application". Note that this entry should be an Array.
  3. Then add the names of these files as elements of this array.

And that's it! All you need then is to use the font by its name like this

cell.textLabel.font = [UIFont fontWithName:@"FontName" size:16];

How to change the font color?

Even easier. All you need is

cell.textlabel.textcolor = UIColor.redColor()

Edit:

In your case you want to change the font of the RowAction. So I think of only 2 solutions. One to use [UIColor colorWithPatterImage:]

Or you can user [[UIButton appearance] setTitleColor:[UIColor orangeColor] forState:UIControlStateNormal]; because the RowAction contains a button.

Community
  • 1
  • 1
Ashraf Tawfeeq
  • 3,036
  • 1
  • 20
  • 34
  • Thank you, but this is not the right answer what I need. You help me implement custom font, that I know. I need set color/font for the cell in UITableViewActionRow. (the pink here: [https://www.dropbox.com/s/jaineb3qgcezm90/Screenshot%202015-03-15%2009.14.51.png?dl=0](https://www.dropbox.com/s/jaineb3qgcezm90/Screenshot%202015-03-15%2009.14.51.png?dl=0)) The code what you wrote me not working, because in the action what you can see in my first post here, you can work just with "title" but the title is reserved for localization only. Any idea how to fix it? – Vlastimil Fiser Mar 15 '15 at 02:20
  • 1
    OMG! You made my day buddy! Thanks. I just rewrote your code to the Swift version and all working perfectly. `UIButton.appearance().setTitleColor(UIColor.orangeColor(), forState: UIControlState.Normal)` **EDIT:** And do you know, how I set the custom font for the button? I can't find function like "setTitleFont" there. – Vlastimil Fiser Mar 15 '15 at 07:14
  • I guess something like the following: `NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:[UIFont fontWithName:@"YOURFONT" size:14], NSFontAttributeName, [UIColor whiteColor], NSForegroundColorAttributeName, nil]; [[UIButton appearance] setTitleTextAttributes:attributes];` – Ashraf Tawfeeq Mar 15 '15 at 07:20
  • Do you know the code in Swift? I can't do that without errors. :/ – Vlastimil Fiser Mar 15 '15 at 07:25
  • I guess it would be something like this ` let attributes: Dictionary = [NSFontAttributeName: UIFont(name: "HelveticaNeue-Light", size: 19)!] UIButton.appearance().setValuesForKeysWithDictionary(attributes)` – Ashraf Tawfeeq Mar 15 '15 at 07:52
  • The app crash, if I paste the code. And one more thing here: this scope is for all buttons in the app. Can I specified it just for the "Delete" button? – Vlastimil Fiser Mar 15 '15 at 09:21
  • Sadly, I can get a hand on the reference. We are making a workaround. Try this line `UIButton.appearance().setValueForKeyPath` and your keyPath is Button.font – Ashraf Tawfeeq Mar 15 '15 at 09:51
  • 1
    I can't use `setValueForKeyPath` on the button, it works for you?.... I'm trying something like this `UIButton.appearance().titleLabel?.font = UIFont(name: "Futura", size: 40)` but still not working, is it without error, but the size and font is still on default. – Vlastimil Fiser Mar 15 '15 at 13:01
  • Hey buddy, any idea there? :)) Thanks a lot! – Vlastimil Fiser Mar 16 '15 at 09:59
  • Hey, pal! Sorry was a heavy day for me. Will dig that for you. :) – Ashraf Tawfeeq Mar 16 '15 at 15:31
  • I have found a solution that you may not like. You can adjust the appearance of UILabel as well to include the font. – Ashraf Tawfeeq Mar 16 '15 at 15:43
  • I'm not understand what you mean now. Can you show me? I want just change the font, with easy way like the font color. – Vlastimil Fiser Mar 16 '15 at 15:56
  • Try this one UILabel.appearance().font = UIFont(name: "", size: 17) – Ashraf Tawfeeq Mar 16 '15 at 16:05
  • Yep, that is not optimal solution, but still not working. I'm just trying it right now and doesn't work. :( – Vlastimil Fiser Mar 16 '15 at 18:00