Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/dk/kil 84 new bubble asset #9

Merged
merged 8 commits into from
Apr 26, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
"images" : [
{
"idiom" : "universal",
"filename" : "left_bubble_orange.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "bubble_left.png",
"scale" : "2x"
},
{
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion Demo/Assets.xcassets/bubble_orange.imageset/Contents.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
"images" : [
{
"idiom" : "universal",
"filename" : "bubble_orange.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "bubble_orange.png",
"scale" : "2x"
},
{
Expand Down
Binary file not shown.
21 changes: 0 additions & 21 deletions Demo/Assets.xcassets/right_bubble_orange.imageset/Contents.json

This file was deleted.

Binary file not shown.
8 changes: 4 additions & 4 deletions Demo/Base.lproj/Main.storyboard
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12118" systemVersion="16E195" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11762" systemVersion="15G1217" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12086"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
Expand Down Expand Up @@ -47,6 +47,6 @@
</scene>
</scenes>
<resources>
<image name="bubble_orange" width="100" height="100"/>
<image name="bubble_orange" width="50" height="50"/>
</resources>
</document>
39 changes: 6 additions & 33 deletions Demo/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,47 +19,20 @@ class ViewController: UIViewController {
messagesView.delegate = self
messagesView.dataSource = self

addCustomMessageBubbles()
//addCustomMessageBubbles()
}

override func viewDidAppear(_ animated: Bool) {
messagesView.scrollToLastMessage(animated: false)
}

func addCustomMessageBubbles() {
let leftBubbleBackgroundImage = UIImage(named: "left_bubble_orange") ?? UIImage()

let wholeCropRect = CGRect(origin: CGPoint(x: 0,y: 0), size: leftBubbleBackgroundImage.size)
let wholeResizeInsets = UIEdgeInsets(top: 25, left: 30, bottom: 25, right: 30)
let wholeSlice = ImageSlice(cropRect: wholeCropRect, resizeInsets: wholeResizeInsets)
let leftBubble = BubbleImage(image: UIImage(named: "bubble_left")!,
resizeInsets: UIEdgeInsets(top: 4, left: 21, bottom: 14, right: 4),
textInsets: UIEdgeInsets(top: 10, left: 22, bottom: 10, right: 6))

let topSliceCropRect = CGRect(x: 0, y: 0, width: 100, height: 30)
let topSliceResizeInsets = UIEdgeInsets(top: 25, left: 30, bottom: 0, right: 30)
let topSlice = ImageSlice(cropRect: topSliceCropRect, resizeInsets: topSliceResizeInsets)

let middleSliceCropRect = CGRect(x: 0, y: 10, width: 100, height: 20)
let middleSliceResizeInsets = UIEdgeInsets(top: 0, left: 25, bottom: 25, right: 25)
let middleSlice = ImageSlice(cropRect: middleSliceCropRect, resizeInsets: middleSliceResizeInsets)

let bottomSliceCropRect = CGRect(x: 0, y: 20, width: 100, height: 90)
let bottomSliceResizeInsets = UIEdgeInsets(top: 0, left: 25, bottom: 25, right: 25)
let bottomSlice = ImageSlice(cropRect: bottomSliceCropRect, resizeInsets: bottomSliceResizeInsets)

let leftBubbleSettings = MessagesViewBubbleSettings(image: leftBubbleBackgroundImage,
whole: wholeSlice,
top: topSlice,
middle: middleSlice,
bottom: bottomSlice)
leftBubbleSettings.textMargin.left = 20

let rightBubbleBackgroundImage = UIImage(named: "right_bubble_orange") ?? UIImage()
let rightBubbleSettings = MessagesViewBubbleSettings(image: rightBubbleBackgroundImage,
whole: wholeSlice,
top: topSlice,
middle: middleSlice,
bottom: bottomSlice)

messagesView.setBubbleImageWith(leftSettings: leftBubbleSettings, rightSettings: rightBubbleSettings)
messagesView.setBubbleImagesWith(left: leftBubble)
}
}

Expand Down Expand Up @@ -93,7 +66,7 @@ extension ViewController: MessagesViewDataSource {
var messages: [MessagesViewChatMessage] {
return TestData.exampleMessageText.enumerated().map { (index, element) in
let peer = self.peers[index % peers.count]
return Message(text: element, sender: peer, onRight: index%2 == 0)
return Message(text: element, sender: peer, onRight: index != 0)
}
}
}
Expand Down
12 changes: 8 additions & 4 deletions MessagesView.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
052769E61EA8F360009AB833 /* UIImage+Flipped.swift in Sources */ = {isa = PBXBuildFile; fileRef = 052769E51EA8F360009AB833 /* UIImage+Flipped.swift */; };
0553B2741E9CF92E00E76010 /* BubbleImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0553B2731E9CF92E00E76010 /* BubbleImage.swift */; };
055DA17F1E9296600091279C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 055DA17E1E9296600091279C /* AppDelegate.swift */; };
055DA1811E9296600091279C /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 055DA1801E9296600091279C /* ViewController.swift */; };
Expand All @@ -18,7 +19,7 @@
055DA1B21E929CCD0091279C /* TestData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 055DA1B11E929CCD0091279C /* TestData.swift */; };
05925ADF1E82D21B00421928 /* MessagesView.swift in Headers */ = {isa = PBXBuildFile; fileRef = 05ECAFA51E7ADFEF00833D84 /* MessagesView.swift */; settings = {ATTRIBUTES = (Public, ); }; };
05925AE71E82E24A00421928 /* MessagesViewSettings.swift in Headers */ = {isa = PBXBuildFile; fileRef = 05ECAFAA1E7ADFEF00833D84 /* MessagesViewSettings.swift */; settings = {ATTRIBUTES = (Public, ); }; };
05B94A351E842FC400CAB715 /* ColorUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05B94A341E842FC400CAB715 /* ColorUtils.swift */; };
05B94A351E842FC400CAB715 /* UIColor+RGB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05B94A341E842FC400CAB715 /* UIColor+RGB.swift */; };
05ECAF8E1E7ADF8400833D84 /* MessagesView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05ECAF841E7ADF8400833D84 /* MessagesView.framework */; };
05ECAF931E7ADF8400833D84 /* MessagesViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05ECAF921E7ADF8400833D84 /* MessagesViewTests.swift */; };
05ECAF951E7ADF8400833D84 /* MessagesView.h in Headers */ = {isa = PBXBuildFile; fileRef = 05ECAF871E7ADF8400833D84 /* MessagesView.h */; settings = {ATTRIBUTES = (Public, ); }; };
Expand Down Expand Up @@ -66,6 +67,7 @@
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
052769E51EA8F360009AB833 /* UIImage+Flipped.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+Flipped.swift"; sourceTree = "<group>"; };
0553B2731E9CF92E00E76010 /* BubbleImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BubbleImage.swift; sourceTree = "<group>"; };
055DA17C1E9296600091279C /* Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Demo.app; sourceTree = BUILT_PRODUCTS_DIR; };
055DA17E1E9296600091279C /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
Expand All @@ -79,7 +81,7 @@
055DA19E1E9296600091279C /* DemoUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoUITests.swift; sourceTree = "<group>"; };
055DA1A01E9296600091279C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
055DA1B11E929CCD0091279C /* TestData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestData.swift; sourceTree = "<group>"; };
05B94A341E842FC400CAB715 /* ColorUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColorUtils.swift; sourceTree = "<group>"; };
05B94A341E842FC400CAB715 /* UIColor+RGB.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+RGB.swift"; sourceTree = "<group>"; };
05ECAF841E7ADF8400833D84 /* MessagesView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MessagesView.framework; sourceTree = BUILT_PRODUCTS_DIR; };
05ECAF871E7ADF8400833D84 /* MessagesView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MessagesView.h; sourceTree = "<group>"; };
05ECAF881E7ADF8400833D84 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
Expand Down Expand Up @@ -183,7 +185,8 @@
isa = PBXGroup;
children = (
05ECAF871E7ADF8400833D84 /* MessagesView.h */,
05B94A341E842FC400CAB715 /* ColorUtils.swift */,
05B94A341E842FC400CAB715 /* UIColor+RGB.swift */,
052769E51EA8F360009AB833 /* UIImage+Flipped.swift */,
05ECAF881E7ADF8400833D84 /* Info.plist */,
05ECAF9E1E7ADFEF00833D84 /* MessageCollectionViewCell.swift */,
05ECAF9F1E7ADFEF00833D84 /* MessageCollectionViewCell.xib */,
Expand Down Expand Up @@ -374,9 +377,10 @@
buildActionMask = 2147483647;
files = (
05ECAFB01E7ADFEF00833D84 /* MessagesToolbarContentView.swift in Sources */,
052769E61EA8F360009AB833 /* UIImage+Flipped.swift in Sources */,
05ECAFAF1E7ADFEF00833D84 /* MessagesInputToolbar.swift in Sources */,
05ECAFB21E7ADFEF00833D84 /* MessagesView.swift in Sources */,
05B94A351E842FC400CAB715 /* ColorUtils.swift in Sources */,
05B94A351E842FC400CAB715 /* UIColor+RGB.swift in Sources */,
0553B2741E9CF92E00E76010 /* BubbleImage.swift in Sources */,
05ECAFAD1E7ADFEF00833D84 /* MessageEditorTextView.swift in Sources */,
05ECAFB71E7ADFEF00833D84 /* MessagesViewSettings.swift in Sources */,
Expand Down
159 changes: 127 additions & 32 deletions MessagesView/BubbleImage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,50 +8,145 @@

import Foundation

public struct ImageSlice {
let cropRect: CGRect
let resizeInsets: UIEdgeInsets
public class BubbleImage {

public init(cropRect: CGRect, resizeInsets: UIEdgeInsets) {
self.cropRect = cropRect
self.resizeInsets = resizeInsets
private enum Slice {
case whole
case top
case middle
case bottom
}
}

// four slices: whole, top, middle, bottom

class BubbleImage {
private var image: UIImage

private var wholeSlice: ImageSlice
private var topSlice: ImageSlice?
private var middleSlice: ImageSlice?
private var bottomSlice: ImageSlice?
public let image: UIImage

public let resizeInsets: UIEdgeInsets
public let textInsets: UIEdgeInsets

lazy var whole: UIImage = self.cropAndResize(slice: self.wholeSlice)
lazy var top: UIImage? = self.cropAndResize(slice: self.topSlice)
lazy var middle: UIImage? = self.cropAndResize(slice: self.middleSlice)
lazy var bottom: UIImage? = self.cropAndResize(slice: self.bottomSlice)
public lazy var whole: UIImage? = self.cropAndResize(slice: .whole)
public lazy var top: UIImage? = self.cropAndResize(slice: .top)
public lazy var middle: UIImage? = self.cropAndResize(slice: .middle)
public lazy var bottom: UIImage? = self.cropAndResize(slice: .bottom)

var textMargin = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
var flipped: BubbleImage {

let flippedImage = image.flipped
let flippedResizeInsets = insetsFlippedHorizontally(resizeInsets)
let flippedTextInsets = insetsFlippedHorizontally(textInsets)

return BubbleImage(image: flippedImage, resizeInsets: flippedResizeInsets, textInsets: flippedTextInsets)
}

required init(image: UIImage, whole: ImageSlice, top: ImageSlice?, middle: ImageSlice?, bottom: ImageSlice?) {
required public init(image: UIImage, resizeInsets: UIEdgeInsets, textInsets: UIEdgeInsets) {
self.image = image
self.wholeSlice = whole
self.topSlice = top
self.middleSlice = middle
self.bottomSlice = bottom
self.resizeInsets = resizeInsets
self.textInsets = textInsets
}

public convenience init(cornerRadius: CGFloat) {

let image = BubbleImage.defaultBubbleImage(cornerRadius: cornerRadius)

let resizeInsets = UIEdgeInsets(top: cornerRadius,
left: cornerRadius * 3,
bottom: cornerRadius * 2,
right: cornerRadius)

let textInsets = UIEdgeInsets(top: cornerRadius,
left: cornerRadius * 3,
bottom: cornerRadius,
right: cornerRadius)

self.init(image: image, resizeInsets: resizeInsets, textInsets: textInsets)
}

convenience init(settings: MessagesViewBubbleSettings) {
self.init(image: settings.image, whole: settings.wholeSlice, top: settings.topSlice, middle: settings.middleSlice, bottom: settings.bottomSlice)
private func insetsFlippedHorizontally(_ edgeInsets: UIEdgeInsets) -> UIEdgeInsets {
return UIEdgeInsets(top: edgeInsets.top,
left: edgeInsets.right,
bottom: edgeInsets.bottom,
right: edgeInsets.left)
}

private func cropAndResize(slice: ImageSlice?)->UIImage {
guard let slice = slice, let croppedImage = image.cgImage?.cropping(to: slice.cropRect) else {
return UIImage()
private func cropAndResize(slice: Slice) -> UIImage? {

let width = image.size.width * image.scale
let height = image.size.height * image.scale

let scaledResizeInsets = UIEdgeInsets(top: resizeInsets.top * image.scale,
left: resizeInsets.left * image.scale,
bottom: resizeInsets.bottom * image.scale,
right: resizeInsets.right * image.scale)

let middleHeight = height - scaledResizeInsets.top - scaledResizeInsets.bottom

let capInsets: UIEdgeInsets
let cropRect: CGRect

switch slice {
case .whole:
capInsets = resizeInsets
cropRect = CGRect(x: 0, y: 0, width: width, height: height)
case .top:
capInsets = UIEdgeInsets(top: resizeInsets.top, left: resizeInsets.left, bottom: 0, right: resizeInsets.right)
cropRect = CGRect(x: 0, y: 0, width: width, height: scaledResizeInsets.top + middleHeight)
case .middle:
capInsets = UIEdgeInsets(top: 0, left: resizeInsets.left, bottom: 0, right: resizeInsets.right)
cropRect = CGRect(x: 0, y: scaledResizeInsets.top, width: width, height: middleHeight)
case .bottom:
capInsets = UIEdgeInsets(top: 0, left: resizeInsets.left, bottom: resizeInsets.bottom, right: resizeInsets.right)
cropRect = CGRect(x: 0, y: scaledResizeInsets.top, width: width, height: scaledResizeInsets.bottom + middleHeight)
}

return UIImage(cgImage: croppedImage).resizableImage(withCapInsets: slice.resizeInsets, resizingMode: .stretch).withRenderingMode(.alwaysTemplate)
guard let croppedImage = image.cgImage?.cropping(to: cropRect) else {
return nil
}
let cropped = UIImage(cgImage: croppedImage, scale: image.scale, orientation: .up)


return cropped.resizableImage(withCapInsets: capInsets, resizingMode: .stretch).withRenderingMode(.alwaysTemplate)
}

public static func defaultBubbleImage(cornerRadius: CGFloat) -> UIImage {

let size = CGSize(width: cornerRadius * 4 + 1, height: cornerRadius * 3 + 1)

UIGraphicsBeginImageContextWithOptions(size, false, 0.0)

UIColor.red.setFill()
UIColor.red.setStroke()

let tailSize = CGSize(width: cornerRadius * 2, height: cornerRadius * 2)
let bubbleSize = CGSize(width: size.width - tailSize.width, height: size.height)

let bubblePath = UIBezierPath(roundedRect: CGRect(origin: CGPoint(x: tailSize.width, y: 0), size: bubbleSize), byRoundingCorners: [.topLeft, .topRight, .bottomRight], cornerRadii: CGSize(width: cornerRadius, height: cornerRadius))
bubblePath.fill()
bubblePath.lineWidth = 1
bubblePath.stroke()

let tailPath = createTailPathIn(origin: CGPoint(x: 0, y: size.height - tailSize.height), size: tailSize)
tailPath.fill()
tailPath.stroke()

let result = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()

return result
}

public static func createTailPathIn(origin: CGPoint, size: CGSize) -> UIBezierPath {
let width = size.width
let height = size.height

let path = UIBezierPath()

path.lineWidth = 1

path.move(to: CGPoint(x: 0.0, y: height))
path.addQuadCurve(to: CGPoint(x: width, y: 0), controlPoint: CGPoint(x: width - 1, y: height - 1))
path.addLine(to: CGPoint(x: width, y: height))
path.close()

path.apply(CGAffineTransform(translationX: origin.x, y: origin.y))

return path
}
}
Loading