2016-09-26 9 views
1

사용자가 손가락 제스처 또는 연필로 문서에 "그리기"할 수있는 주석 도구가 포함 된 프로젝트 작업 중입니다. 당연히, 나는 그려진 경로에 대해 실행 취소/다시 실행을 구현하는 데 열중하고 있습니다.드로잉 응용 프로그램에서 UndoManager를 사용하여 다시 실행 구현 Swift 3

드로잉 앱에 대한 구현은 비교적 일반적입니다. 사용자가 화면에서 보는 것은 캐싱 된 비트 맵 이미지 (현재 경로보다 먼저 그려진 모든 경로의 스냅 샷)와 현재 경로의 "라이브"렌더링 (UIBezierPath)의 조합입니다. touchesEnded가 트리거되면 새 경로가 비트 맵에 추가됩니다.

나는 비교적 적은 문제만으로 실행 취소를 구현할 수있었습니다. 나는 클래스의 표준를 UndoManager를 만들었습니다

let myUndoManager : UndoManager = { 
    let mUM : UndoManager = UndoManager() 
    mUM.levelsOfUndo = 6 
    return mUM 
}() 

새로운 캐시 경로를 렌더링하는 touchesEnded의 끝에서 호출되는 함수가 drawBitmap이라고합니다. 이 함수의 시작에서, 가정 이전 캐시 경로가 새로운 하나를 그리기 전에, 내가 실행 취소 관리자와 다음 실행 취소 작업 등록 :

let previousCachedPath : UIImage = self.cachedPath 
self.myUndoManager.registerUndo(withTarget: self, selector: #selector(self.setBitmap(_:)), object: previousCachedPath) 

setBitmap을 (_ previousCachedPath :있는 UIImage)는 기능입니다 표시된 비트 맵을 제공된 이미지로 재설정합니다.

각각 undo() 및 redo() 메소드에 연결된 실행 취소/다시 실행 버튼이 있습니다. 이 버튼이 활성화되어야 할 때를 명령하는 로직을 제외하고 (즉, 아무 것도 그리지 않았을 때 실행 취소를 누르지 못하도록하기 위해) 이들은 단순히 myUndoManager.undo() 및 myUndoManager.redo()를 호출합니다.

func undo() -> Void { 
    guard self.myUndoManager.canUndo else { return } 
    self.myUndoManager.undo() 
    if !self.redoButton.isEnabled { 
     self.redoButton.isEnabled = true 
    } 
    if !self.myUndoManager.canUndo { 
     self.undoButton.isEnabled = false 
    } 

    self.setNeedsDisplay() 
} 

func redo() -> Void { 
    guard self.myUndoManager.canRedo else { return } 
    self.myUndoManager.redo() 
    if !self.undoButton.isEnabled { 
     self.undoButton.isEnabled = true 
    } 
    if !self.myUndoManager.canRedo { 
     self.redoButton.isEnabled = false 
    } 

    self.setNeedsDisplay() 
} 

앞서 언급했듯이 실행 취소는 지정된 6 단계 실행 취소 가능성에 완벽하게 작동합니다. 그러나, 나는 분명히 redo로 무언가를 놓치고있다. 내 첫 번째 희망은 undoManager가 undo가 호출 될 때 undo 작업을 undo 스택에서 redo 스택으로 자동 전송한다는 것이었지만, 이것은 분명히 일어나지 않았다.

나는 이미 답을 검색 한, 그리고 내가 따라 상호 재귀를 사용할 수 있습니다 필요에 가장 가까운 생각 :

Using NSUndoManager, how to register undos using Swift closures

는 그러나, 나는이 일을 할 수 없었습니다. 따라서 어떤 도움을 주셔서 감사합니다!

+0

트릭은 _one_ 메소드 만 있어야하며 실행 취소와 다시 실행 모두 호출해야한다는 것입니다. 다시 실행 취소는 실행 취소입니다. 실행 취소의 실행 취소는 다시 실행입니다. 그것은 내 책의 장을 읽는 것을 도울 것입니다 : http://www.apeth.com/iOSBook/ch39.html – matt

+0

감사합니다 - 나는 오늘 아침에 책을 읽게 될 것이고 여러분이 어떻게 시작하는지 알려줄 것입니다! – Sparky

+0

@matt 고마워요. 완벽하게 작동했습니다. 당신이 제안했듯이 모든 것을 하나의 메서드로 결합했습니다.이 경우에는 setBitmap (_ :)입니다. 내가 설명한 방법 중 하나를 사용하여 작업 할 수있게되었습니다. 나는 다른 사람들을 돕기 위해 내 솔루션을 게시했습니다. – Sparky

답변

4

@matt의 도움 덕분에 모든 것을 setBitmap (_ :) 함수에 넣음으로써이 문제를 해결했습니다. 선택 : 방법 :

func setBitmap(_ toCachedPath : UIImage) -> Void { 
    self.myUndoManager.registerUndo(withTarget: self, selector: #selector(self.setBitmap(_:)), object: self.cachedPath) 

    UIGraphicsBeginImageContextWithOptions(self.bounds.size, false, 0.0) 
    toCachedPath.draw(at: CGPoint.zero)  
    self.cachedPath = UIGraphicsGetImageFromCurrentImageContext() 
    UIGraphicsEndImageContext() 
} 

그리고 또한 준비 (withInvocationTarget :) 접근 방식 :

func setBitmap(_ toCachedPath : UIImage) -> Void { 
    if self.cachedPath != nil { 
     (self.rWUndoManager.prepare(withInvocationTarget: self) as AnyObject).setBitmap(self.cachedPath) 
    } 

    UIGraphicsBeginImageContextWithOptions(self.bounds.size, false, 0.0) 
    toCachedPath.draw(at: CGPoint.zero) 
    self.cachedPath = UIGraphicsGetImageFromCurrentImageContext() 
    UIGraphicsEndImageContext() 
} 

희망 다른 사람을 도움이 시도하고 더 나은 일을 이해하기 위해, 나는 registerUndo (withTarget 모두 구현 내가 그랬던 것처럼 머리를 긁적 인 사람.

0

사실 다시 실행을 위해 클로저 버전 registerUndo(withTarget:handler:)을 사용할 수도 있습니다. 먼저 셀렉터 1 registerUndo(withTarget:selector:object:)에서 작동하는지 확인하십시오. 즉, 답변 에서처럼 매개 변수 하나만 사용하는 함수를 만듭니다.그리고 당신은 폐쇄 하나를 사용하여 선택 - 기반 방법을 대체 할 수

func setBitmap(_ toCachedPath : UIImage) -> Void { 
    let oldCachedPath = cachedPath // For not referencing it with `self` in the closure. 
    myUndoManager.registerUndo(withTarget: self) { 
     $0.setBitmap(oldCachedPath) 
    } 

    UIGraphicsBeginImageContextWithOptions(self.bounds.size, false, 0.0) 
    toCachedPath.draw(at: CGPoint.zero)  
    self.cachedPath = UIGraphicsGetImageFromCurrentImageContext() 
    UIGraphicsEndImageContext() 
} 

내가 폐쇄 버전은 단지 하나의 방법 및 하나 개의 입력 매개 변수를 인식 선택기 버전의 리 패키지 것 같다, 그래서 우리는 여전히에있다 그것을 위해 이렇게 쓰십시오.