Custom iOS chat interface design & functionality
[ChatController new];
This is perfectly fine syntax, but an Objective-C purists doesn't necessarily like the new method that much.  new is simply an NSObject method that looks like this:
+ (instancetype)new {
    return [[self alloc] init];
}

And as such, anything that inherits from NSObject will have this method.  But the only reason it exists as far as I know is for something like a security blanket for people coming from other languages where object instantiation might look something like this:
ChatController *chatController = new ChatController();

They want to hang on to their new keyword--their clue that they're instantiating a new object.  And while this is fine for those who choose to use it, a complete class would write its own factory method(s) in addition to the default one inherited from NSObject.
NSArray, NSDictionary, NSNumber, NSDate, etc, etc, etc.  Most of the Foundation classes have their own method that does something similar to what the new method does.  You would be wise to follow suite with a method that looks something like this:
+ (instancetype)chatController {
    return [[self alloc] init];
}

Now, someone like me who prefers doing things the real Objective-C way can do [ChatController chatController] in place of new.
As someone who might use a library like yours, the existence of such a method tells me that you've paid some attention to your instantiation methods and aren't relying on those inherited from NSObject.  This assures me that any instance variables that need to be set before I call any methods (if any at all) are definitely going to be set.
As a note, if you override init, this instance variables will still be set when calling new, because remember, new is simply return [[self alloc] init]; so it calls the init method you wrote.

The fact of the matter is though, whether we use [ChatController new];, [ChatController alloc] init];, or [ChatController chatController];, your class isn't completely ready to use.
What would happen if I presented an instance of your ChatController class before I set the delegate, chatTitle, and opponentImg?  Hopefully, you've written the class in such a way that nil or an empty string is a perfectly fine title, and that nil for opponentImg causes no problems.  But what about the delegate?
Sometimes, objects that can have a delegate function somewhat fine without a delegate.  For example, a tableview has a delegate property, but it also has a datasource property.  The datasource fills the table with information.  The delegate handles user interaction with the table.  But if you don't need to respond to any of the user interactions and only need the table to display information to a user, your table will function just fine without a delegate.  (And a table with static information doesn't need a datasource--the static cells can be completely created in IB).
Is this the case with your ChatController?  Can it function just fine without a delegate?
If these three properties are all truly optional and the class works just fine if they're all set to nil, then you're fine as you are and the following information would be something optional to consider, purely a nicety (one which I always include in my classes).  However, if these properties are not optional and it's not okay to set them to nil, then you really need to implement some factory methods.
Assuming only the delegate is necessary, first we'll create a new init method:
- (id)initWithDelegate:(id&lt;ChatControllerDelegate&gt;)delegate {
    self = [super init];
    if (self) {
        _delegate = delegate;
    }
    return self;
}

This method takes the delegate as an argument and sets this property upon while it's doing its initializing.  But as is, we have to write:
ChatController *chatController = [[ChatController alloc] initWithDelegate:self];

So let's wrap it in a factory method:
+ (instancetype)chatControllerWithDelegate:(id&lt;ChatControllerDelegate&gt;)delegate {
    return [[self alloc] initWithDelegate:delegate];
}

Now, it's as simple as this:
ChatController *chatController = [ChatController chatControllerWithDelegate:self];

Following this same pattern, you should include a factory method that sets all the properties that a user is likely to want to set before doing anything with the object.  For example:
- (id)initWithDelegate:(id&lt;ChatControllerDelegate&gt;)delegate
             chatTitle:(NSString*)chatTitle
         opponentImage:(UIImage*)opponentImage {
    self = [super init];
    if (self) {
        _delegate = delegate;
        _chatTitle = chatTitle;
        _opponentImage = opponentImage;
    }
    return self;
}

+ (instancetype)chatControllerWithDelegate:(id&lt;ChatControllerDelegate&gt;)delegate
                                 chatTitle:(NSString*)chatTitle
                             opponentImage:(NSString*)opponentImage {
    return [[self alloc] initWithDelegate:delegate
                                chatTitle:chatTitle
                            opponentImage:opponentImage];
}